packages feed

keid-render-basic 0.1.10.0 → 0.1.12.0

raw patch · 33 files changed

+955/−800 lines, 33 filesdep +kb-text-shapedep +ktx-fontdep +msdf-atlasdep ~riobinary-added

Dependencies added: kb-text-shape, ktx-font, msdf-atlas

Dependency ranges changed: rio

Files

ChangeLog.md view
@@ -1,5 +1,38 @@ # Changelog for keid-render-basic +## 0.1.12.0++Moving from EvanwSdf atlases (thanks Evan!), to MTSDF by Chlumsky.+Atlas generation now doesn't require using browser and copypasting.++- Added `Resource.Font.Ktxf` for the texture+atlas+shaping font container.+  * New deps: `ktx-font`, `msdf-atlas`, `kb-text-shape`.+  * Text is shaped once with `Ktxf.shape` and placed with the pure `Ktxf.place`,+    which wraps and aligns the lines using `kb-text-layout` via `ktx-font`.+  * `Ktxf.Stack` pairs the shaping context with its measuring style.+    `allocateStack1`/`allocateMonoStacks` produce those.+- Removed `Resource.Font.EvanwSdf.*` (models and `pEvanwSdf` pipeline).+- Added `Resource.Font.Msdf.*` models and (`pMsdf`) pipeline.+- Changed `Engine.UI.Message.Input.inputFont` field to use textureId-augmented font stacks (`Ktxf.Stack`).+- Added `Engine.UI.Message.Input.inputBreak` field to pick the line breaking strategy.+  Long lines now wrap to the box width and align per line.+- Removed `Engine.UI.Message.Input.inputFontId` field, now redundant.+- Added Base{white} built-in texture.+  * Breaking change as it will alter the indices that come after it.++## 0.1.11.0++- Added specialization constants to common Lit code (thus, all the pipelines).+- Added Lit specialization to Basic.Settings.+- Moved antialiasing settings (msaa, sampler anisotropy) to Basic.Settings.+  + Use new `Basic.swapchainSettings` instead of passing swapchain as arguments.+- Refactored external pipelines to use one loader for all the variants.+  + External pipelines now react to change in the specialization constants.+- Fixed AO texture application in Material.+  + With 0 being most occluded, and 1 fully open.+  + Values are multiplicative now, like the rest of the channels.+- Changed Fresnel function for lights to use the roughness-aware version.+ ## 0.1.10.0  - Update to follow the changes in keid-core-0.1.11 and geomancy-0.3.
− embed/textures/black.ktx.zst

binary file changed (67 → absent bytes)

− embed/textures/flat.ktx.zst

binary file changed (97 → absent bytes)

− embed/textures/ibl_brdf_lut.ktx.zst

binary file changed (192701 → absent bytes)

+ embed/textures/white.ktx2 view

binary file changed (absent → 264 bytes)

keid-render-basic.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.38.1.+-- This file has been generated from package.yaml by hpack version 0.36.1. -- -- see: https://github.com/sol/hpack  name:           keid-render-basic-version:        0.1.10.0+version:        0.1.12.0 synopsis:       Basic rendering programs for Keid engine. category:       Game Engine homepage:       https://keid.haskell-game.dev@@ -21,12 +21,10 @@ data-files:     embed/cubemaps/black.ktx.zst     embed/cubemaps/black.ktx2-    embed/textures/black.ktx.zst     embed/textures/black.ktx2-    embed/textures/flat.ktx.zst     embed/textures/flat.ktx2-    embed/textures/ibl_brdf_lut.ktx.zst     embed/textures/ibl_brdf_lut.ktx2+    embed/textures/white.ktx2  source-repository head   type: git@@ -49,9 +47,9 @@       Render.DescSets.Set0       Render.DescSets.Set0.Code       Render.DescSets.Sun-      Render.Font.EvanwSdf.Code-      Render.Font.EvanwSdf.Model-      Render.Font.EvanwSdf.Pipeline+      Render.Font.Msdf.Code+      Render.Font.Msdf.Model+      Render.Font.Msdf.Pipeline       Render.ForwardMsaa       Render.Lit.Colored.Code       Render.Lit.Colored.Model@@ -61,6 +59,7 @@       Render.Lit.Material.Collect       Render.Lit.Material.Model       Render.Lit.Material.Pipeline+      Render.Lit.Specialization       Render.Lit.Textured.Code       Render.Lit.Textured.Model       Render.Lit.Textured.Pipeline@@ -87,7 +86,7 @@       Render.Unlit.TileMap.Model       Render.Unlit.TileMap.Pipeline       Resource.Font-      Resource.Font.EvanW+      Resource.Font.Ktxf       Resource.Mesh.Lit       Stage.Loader.Render       Stage.Loader.Scene@@ -135,11 +134,14 @@     , geomancy >=0.3.0.0     , geomancy-layout     , gl-block+    , kb-text-shape     , keid-core >=0.1.11.0     , keid-geometry+    , ktx-font+    , msdf-atlas     , neat-interpolation     , resourcet-    , rio >=0.1.12.0+    , rio >=0.1.24.0     , tagged     , text     , unliftio
src/Engine/UI/Message.hs view
@@ -15,10 +15,8 @@  import Control.Monad.Trans.Resource (ResourceT) import Control.Monad.Trans.Resource qualified as Resource-import Data.Text qualified as Text-import Data.Type.Equality (type (~)) import Data.Vector.Storable qualified as Storable-import Geomancy (Vec4)+import Geomancy (Vec4, ivec2, vec2) import Geomancy.Layout.Alignment (Alignment) import Geomancy.Layout.Box (Box(..)) import Geomancy.Vec4 qualified as Vec4@@ -28,12 +26,12 @@ import Engine.Types qualified as Engine import Engine.Vulkan.Types (MonadVulkan) import Engine.Worker qualified as Worker-import Render.Font.EvanwSdf.Model qualified as EvanwSdf+import Render.Font.Msdf.Model qualified as Msdf import Render.Samplers qualified as Samplers import Resource.Buffer qualified as Buffer-import Resource.Font.EvanW qualified as Font+import Resource.Font.Ktxf qualified as Ktxf -type Process = Worker.Merge (Storable.Vector EvanwSdf.InstanceAttrs)+type Process = Worker.Merge (Storable.Vector Msdf.InstanceAttrs)  spawn   :: ( MonadResource m@@ -46,7 +44,10 @@   => box   -> input   -> m Process-spawn = Worker.spawnMerge2 mkAttrs+spawn box input = do+  shaped <- Worker.merge1M input \Input{inputFont, inputText} ->+    liftIO $ Ktxf.shape inputFont inputText+  Worker.spawnMerge3 mkAttrs box input shaped  spawnFromR   :: ( MonadResource m@@ -65,46 +66,33 @@  data Input = Input   { inputText         :: Text-  , inputFontId       :: Int32-  , inputFont         :: Font.Container+  , inputLineSize     :: Float+  , inputBreak        :: Ktxf.Strategy+  , inputFont         :: Ktxf.Stack   , inputOrigin       :: Alignment   , inputSize         :: Float   , inputColor        :: Vec4   , inputOutline      :: Vec4   , inputOutlineWidth :: Float   , inputSmoothing    :: Float-  }+  } deriving (Generic) -mkAttrs :: Box -> Input -> Storable.Vector EvanwSdf.InstanceAttrs-mkAttrs Box{..} Input{..} =-  if inputSize < 1 then mempty else Storable.fromList do-    Font.PutChar{..} <- chars-    pure EvanwSdf.InstanceAttrs-      { vertRect     = Vec4.fromVec22 pcPos pcSize+mkAttrs :: Box -> Input -> Ktxf.ShapedText -> Storable.Vector Msdf.InstanceAttrs+mkAttrs box Input{..} shaped =+  Storable.fromList do+    Ktxf.PutChar{..} <- Ktxf.place box inputOrigin inputLineSize inputBreak inputSize inputFont shaped+    pure Msdf.InstanceAttrs+      { vertRect     = Vec4.fromVec22 pcPos pcSize -- get from char?       , fragRect     = Vec4.fromVec22 pcOffset pcScale-      , color        = inputColor-      , outlineColor = inputOutline -- vec4 0 0.25 0 0.25-      , textureId    = inputFontId-      , samplerId    = samplerId+      , textureIds    = ivec2 Samplers.indices.linear pcTextureId+      , sdfDist = vec2 pcDistanceLower pcDistanceRange       , smoothing    = inputSmoothing -- 1/16       , outlineWidth = inputOutlineWidth -- 3/16+      , color        = inputColor+      , outlineColor = inputOutline -- vec4 0 0.25 0 0.25       }-  where-    (scale, chars) = Font.putLine-      size-      position-      inputOrigin-      inputSize-      inputFont-      (Text.unpack inputText) -    samplerId =-      if scale > 1 then-        Samplers.linear Samplers.indices-      else-        Samplers.linear Samplers.indices--type Buffer = Buffer.Allocated 'Buffer.Coherent EvanwSdf.InstanceAttrs+type Buffer = Buffer.Allocated 'Buffer.Coherent Msdf.InstanceAttrs  type Observer = Worker.ObserverIO Buffer @@ -123,7 +111,7 @@ observe   :: ( MonadVulkan env m      , Worker.HasOutput source-     , Worker.GetOutput source ~ Storable.Vector EvanwSdf.InstanceAttrs+     , Worker.GetOutput source ~ Storable.Vector Msdf.InstanceAttrs      )   => source   -> Observer
src/Global/Resource/Texture/Base.hs view
@@ -1,17 +1,17 @@ {-# LANGUAGE DeriveAnyClass #-} --- {-# OPTIONS_GHC -fforce-recomp #-}- module Global.Resource.Texture.Base   ( Collection(..)   , Textures   , sources+  , indices   ) where  import RIO  import GHC.Generics (Generic1) import Resource.Collection (Generically1(..))+import Resource.Collection qualified as Collection import Resource.Source (Source) import Resource.Source qualified as Source import Resource.Texture (Texture, Flat)@@ -19,9 +19,10 @@ import Global.Resource.Texture.Base.Paths qualified as Paths  data Collection a = Collection-  { black        :: a-  , flat         :: a-  , ibl_brdf_lut :: a+  { black        :: a -- ^ pure black / fallback texture at index 0+  , flat         :: a -- ^ normal fallback texture+  , ibl_brdf_lut :: a -- ^ BRDF LUT for PBR shaders+  , white        :: a -- ^ pure white   }   deriving stock (Show, Functor, Foldable, Traversable, Generic1)   deriving Applicative via Generically1 Collection@@ -33,4 +34,8 @@   { black        = $(Source.embedFile Paths.BLACK_KTX2)   , flat         = $(Source.embedFile Paths.FLAT_KTX2)   , ibl_brdf_lut = $(Source.embedFile Paths.IBL_BRDF_LUT_KTX2)+  , white        = $(Source.embedFile Paths.WHITE_KTX2)   }++indices :: Collection Int32+indices = fmap fst $ Collection.enumerate sources
src/Render/Basic.hs view
@@ -13,11 +13,9 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Kind (Type) import Data.Tagged (Tagged(..))-import Data.Type.Equality (type (~)) import RIO.FilePath ((</>), (<.>)) import RIO.Vector.Partial as Vector (headM) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (Zero(..))  -- keid-core @@ -31,6 +29,7 @@ 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 @@ -40,11 +39,12 @@ import Render.DescSets.Set0 qualified as Scene import Render.DescSets.Sun (Sun) import Render.DescSets.Sun qualified as Sun-import Render.Font.EvanwSdf.Pipeline qualified as EvanwSdf+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@@ -88,15 +88,21 @@ data Settings = Settings   { sShadowSize   :: Word32   , sShadowLayers :: Word32+  , sMSAA         :: Vk.SampleCountFlagBits+  , sAnisotropy   :: Float+  , sLitSpec      :: Lit.Specialization   }   deriving (Eq, Show) -instance Zero Settings where-  zero = Settings-    -- XXX: 1x1 placeholder image-    { sShadowSize   = 1-    , sShadowLayers = 1-    }+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@@ -123,21 +129,23 @@      )   => swapchain   -> ResourceT (RIO env) RenderPasses-allocate_ = allocate zero+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-  { pMSAA       :: Vk.SampleCountFlagBits+  { pSettings :: f ^ External.Params Settings+   , pSceneBinds :: Tagged Scene DsLayoutBindings   , pSceneLayout :: Tagged '[Scene] Vk.DescriptorSetLayout    , pShadowBinds :: Tagged Sun DsLayoutBindings   , pShadowLayout :: Tagged '[Sun] Vk.DescriptorSetLayout -  , pEvanwSdf :: f ^ EvanwSdf.Pipeline+  , pMsdf :: f ^ Msdf.Pipeline+  , pSoftmask :: f ^ Msdf.Pipeline   , pSkybox   :: f ^ Skybox.Pipeline    , pDebugUV      :: f ^ Debug.Pipeline@@ -176,63 +184,64 @@   => swapchain   -> RenderPasses   -> ResourceT (StageRIO st) Pipelines-allocatePipelines_ =-  allocatePipelinesFor Nothing Nothing+allocatePipelines_ swapchain = allocatePipelinesFor settings Nothing Nothing+  where+    settings = swapchainSettings swapchain  allocatePipelinesFor-  :: ( HasSwapchain swapchain-     , Foldable textures+  :: ( Foldable textures      , Foldable cubes      )-  => textures t+  => Settings+  -> textures t   -> cubes c-  -> swapchain   -> RenderPasses   -> ResourceT (StageRIO st) Pipelines-allocatePipelinesFor textures cubes swapchain renderpasses = do-  samplers <- Samplers.allocate (Swapchain.getAnisotropy swapchain)+allocatePipelinesFor settings textures cubes renderpasses = do+  samplers <- Samplers.allocate settings.sAnisotropy    allocatePipelines+    settings     (Scene.mkBindings samplers textures cubes shadows)-    (Swapchain.getMultisample swapchain)     renderpasses   where     shadows = renderpasses.rpShadowPass.smLayerCount  allocatePipelines-  :: Tagged Scene DsLayoutBindings-  -> Vk.SampleCountFlagBits+  :: Settings+  -> Tagged Scene DsLayoutBindings   -> RenderPasses   -> ResourceT (StageRIO st) Pipelines-allocatePipelines pSceneBinds pMSAA RenderPasses{..} = do-  pEvanwSdf <- EvanwSdf.allocate pMSAA pSceneBinds rpForwardMsaa-  pSkybox   <- Skybox.allocate pMSAA pSceneBinds rpForwardMsaa+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         pMSAA pSceneBinds rpForwardMsaa-  pDebugTexture <- Debug.allocate Debug.Texture    pMSAA pSceneBinds rpForwardMsaa-  pDebugShadow  <- Debug.allocate (Debug.Shadow 1) pMSAA 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 pMSAA pSceneBinds rpForwardMsaa+  pDepthOnly <- DepthOnly.allocate sMSAA pSceneBinds rpForwardMsaa -  pLitColored       <- LitColored.allocate pMSAA pSceneBinds rpForwardMsaa-  pLitColoredBlend  <- LitColored.allocateBlend pMSAA pSceneBinds rpForwardMsaa-  pLitMaterial      <- LitMaterial.allocate pMSAA pSceneBinds rpForwardMsaa-  pLitMaterialBlend <- LitMaterial.allocateBlend pMSAA pSceneBinds rpForwardMsaa-  pLitTextured      <- LitTextured.allocate pMSAA pSceneBinds rpForwardMsaa-  pLitTexturedBlend <- LitTextured.allocateBlend pMSAA 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 pMSAA pSceneBinds rpForwardMsaa-  pLine2dNoDepth       <- UnlitLine2d.allocate False pMSAA pSceneBinds rpForwardMsaa-  pSprite              <- UnlitSprite.allocate pMSAA Nothing False pSceneBinds rpForwardMsaa-  pSpriteOutline       <- UnlitSprite.allocate pMSAA Nothing True pSceneBinds rpForwardMsaa-  pTileMap             <- UnlitTileMap.allocate pMSAA pSceneBinds rpForwardMsaa-  pTileMapBlend        <- UnlitTileMap.allocateBlend pMSAA pSceneBinds rpForwardMsaa-  pUnlitColored        <- UnlitColored.allocate True pMSAA pSceneBinds rpForwardMsaa-  pUnlitColoredNoDepth <- UnlitColored.allocate False pMSAA pSceneBinds rpForwardMsaa-  pUnlitTextured       <- UnlitTextured.allocate pMSAA pSceneBinds rpForwardMsaa-  pUnlitTexturedBlend  <- UnlitTextured.allocateBlend pMSAA pSceneBinds rpForwardMsaa-  pWireframe           <- UnlitColored.allocateWireframe True pMSAA pSceneBinds rpForwardMsaa-  pWireframeNoDepth    <- UnlitColored.allocateWireframe False pMSAA 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@@ -255,25 +264,33 @@   pure Pipelines{..}  allocateWorkers-  :: Tagged Scene DsLayoutBindings-  -> Vk.SampleCountFlagBits+  :: Settings+  -> Tagged Scene DsLayoutBindings   -> RenderPasses   -> ResourceT (StageRIO st) PipelineWorkers-allocateWorkers sceneBinds pMSAA renderPasses = do-  builtin <- allocatePipelines sceneBinds pMSAA renderPasses+allocateWorkers settings sceneBinds renderPasses = do+  builtin <- allocatePipelines settings sceneBinds renderPasses -  let-    evanwSdfFiles =-      Graphics.basicStages-        (shaderDir </> "evanw-sdf" <.> "vert" <.> "spv")-        (shaderDir </> "evanw-sdf" <.> "frag" <.> "spv")+  settingsVar <- Worker.newVar settings+  litSpecUpdates <- Worker.mergeNeq1 settingsVar (.sLitSpec) -  evanwSdfWorker <--    External.spawnReflect "evanw-sdf" evanwSdfFiles \(stageCode, reflect) ->-      (EvanwSdf.config sceneBinds)+  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 =@@ -326,185 +343,167 @@         , Graphics.cReflect = Just reflect         } -  let-    litColoredFiles =-      Graphics.basicStages-        (shaderDir </> "lit-colored" <.> "vert" <.> "spv")-        (shaderDir </> "lit-colored" <.> "frag" <.> "spv")-+  litColoredLoader <- External.spawnReflect_ "lit-colored" $+    Graphics.basicStages+      (shaderDir </> "lit-colored" <.> "vert" <.> "spv")+      (shaderDir </> "lit-colored" <.> "frag" <.> "spv")   litColoredWorker <--    External.spawnReflect "lit-colored" litColoredFiles \(stageCode, reflect) ->-      (LitColored.config sceneBinds)+    Worker.merge2 litSpecUpdates litColoredLoader \litSpec (stageCode, reflect) ->+      (LitColored.config litSpec sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   litColoredBlendWorker <--    External.spawnReflect "lit-colored-blend" litColoredFiles \(stageCode, reflect) ->-      (LitColored.configBlend sceneBinds)+    Worker.merge2 litSpecUpdates litColoredLoader \litSpec (stageCode, reflect) ->+      (LitColored.configBlend litSpec sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  let-    litMaterialFiles =+  litMaterialLoader <-+    External.spawnReflect_ "lit-material" $       Graphics.basicStages         (shaderDir </> "lit-material" <.> "vert" <.> "spv")         (shaderDir </> "lit-material" <.> "frag" <.> "spv")-   litMaterialWorker <--    External.spawnReflect "lit-material" litMaterialFiles \(stageCode, reflect) ->-      (LitMaterial.config sceneBinds)+    Worker.merge2 litSpecUpdates litMaterialLoader \litSpec (stageCode, reflect) ->+      (LitMaterial.config litSpec sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }    litMaterialBlendWorker <--    External.spawnReflect "lit-material-blend" litMaterialFiles \(stageCode, reflect) ->-      (LitMaterial.configBlend sceneBinds)+    Worker.merge2 litSpecUpdates litMaterialLoader \litSpec (stageCode, reflect) ->+      (LitMaterial.configBlend litSpec sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  let-    litTexturedFiles =+  litTexturedLoader <-+    External.spawnReflect_ "lit-textured" $       Graphics.basicStages         (shaderDir </> "lit-textured" <.> "vert" <.> "spv")         (shaderDir </> "lit-textured" <.> "frag" <.> "spv")-   litTexturedWorker <--    External.spawnReflect "lit-textured" litTexturedFiles \(stageCode, reflect) ->-      (LitTextured.config sceneBinds)+    Worker.merge2 litSpecUpdates litTexturedLoader \litSpec (stageCode, reflect) ->+      (LitTextured.config litSpec sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   litTexturedBlendWorker <--    External.spawnReflect "lit-textured-blend" litTexturedFiles \(stageCode, reflect) ->-      (LitTextured.configBlend sceneBinds)+    Worker.merge2 litSpecUpdates litTexturedLoader \litSpec (stageCode, reflect) ->+      (LitTextured.configBlend litSpec sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  let-    line2dFiles =+  line2dLoader <-+    External.spawnReflect_ "line2d" $       Graphics.basicStages         (shaderDir </> "line-2d" <.> "vert" <.> "spv")         (shaderDir </> "line-2d" <.> "frag" <.> "spv")-   line2dWorker <--    External.spawnReflect "unlit-colored" line2dFiles \(stageCode, reflect) ->+    Worker.merge1 line2dLoader \(stageCode, reflect) ->       (UnlitLine2d.config True sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   line2dNoDepthWorker <--    External.spawnReflect "unlit-colored-nodepth" line2dFiles \(stageCode, reflect) ->+    Worker.merge1 line2dLoader \(stageCode, reflect) ->       (UnlitLine2d.config False sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  let-    unlitColoredFiles =+  unlitColoredLoader <-+    External.spawnReflect_ "unlit-colored" $       Graphics.basicStages         (shaderDir </> "unlit-colored" <.> "vert" <.> "spv")         (shaderDir </> "unlit-colored" <.> "frag" <.> "spv")-   unlitColoredWorker <--    External.spawnReflect "unlit-colored" unlitColoredFiles \(stageCode, reflect) ->+     Worker.merge1 unlitColoredLoader \(stageCode, reflect) ->       (UnlitColored.config True sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   unlitColoredNoDepthWorker <--    External.spawnReflect "unlit-colored-nodepth" unlitColoredFiles \(stageCode, reflect) ->+    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+        } -  let-    unlitTexturedFiles =+  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 <--    External.spawnReflect "unlit-textured" unlitTexturedFiles \(stageCode, reflect) ->+    Worker.merge1 unlitTexturedLoader \(stageCode, reflect) ->       (UnlitTextured.config sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   unlitTexturedBlendWorker <--    External.spawnReflect "unlit-textured" unlitTexturedFiles \(stageCode, reflect) ->+    Worker.merge1 unlitTexturedLoader \(stageCode, reflect) ->       (UnlitTextured.configBlend sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  let-    spriteFiles =+  spriteLoader <-+    External.spawnReflect_ "sprite" $       Graphics.basicStages         (shaderDir </> "sprite" <.> "vert" <.> "spv")         (shaderDir </> "sprite" <.> "frag" <.> "spv")-   spriteWorker <--    External.spawnReflect "sprite" spriteFiles \(stageCode, reflect) ->+    Worker.merge1 spriteLoader \(stageCode, reflect) ->       (UnlitSprite.config Nothing False sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   spriteOutlineWorker <--    External.spawnReflect "sprite" spriteFiles \(stageCode, reflect) ->+    Worker.merge1 spriteLoader \(stageCode, reflect) ->       (UnlitSprite.config Nothing True sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  let-    tileMapFiles =+  tileMapLoader <-+    External.spawnReflect_ "tilemap" $       Graphics.basicStages         (shaderDir </> "tilemap" <.> "vert" <.> "spv")         (shaderDir </> "tilemap" <.> "frag" <.> "spv")-   tileMapWorker <--    External.spawnReflect "tilemap" tileMapFiles \(stageCode, reflect) ->+    Worker.merge1 tileMapLoader \(stageCode, reflect) ->       (UnlitTileMap.config sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         }-   tileMapBlendWorker <--    External.spawnReflect "tilemap" tileMapFiles \(stageCode, reflect) ->+    Worker.merge1 tileMapLoader \(stageCode, reflect) ->       (UnlitTileMap.configBlend sceneBinds)         { Graphics.cStages = stageCode         , Graphics.cReflect = Just reflect         } -  wireframeWorker <--    External.spawnReflect "wireframe" unlitColoredFiles \(stageCode, reflect) ->-      (UnlitColored.configWireframe True sceneBinds)-        { Graphics.cStages = stageCode-        , Graphics.cReflect = Just reflect-        }--  wireframeNoDepthWorker <--    External.spawnReflect "wireframe-nodepth" unlitColoredFiles \(stageCode, reflect) ->-      (UnlitColored.configWireframe False 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)@@ -513,31 +512,37 @@         }    pure builtin-    { pEvanwSdf = evanwSdfWorker-    , pSkybox = skyboxWorker-    , pDebugUV = debugUVWorker-    , pDebugTexture = debugTextureWorker-    , pDebugShadow = debugShadowWorker-    , pDepthOnly = depthOnlyWorker-    , pLitColored = litColoredWorker-    , pLitColoredBlend = litColoredBlendWorker-    , pLitMaterial = litMaterialWorker-    , pLitMaterialBlend = litMaterialBlendWorker-    , pLitTextured = litTexturedWorker-    , pLitTexturedBlend = litTexturedBlendWorker-    , pLine2d = line2dWorker-    , pLine2dNoDepth = line2dNoDepthWorker-    , pUnlitColored = unlitColoredWorker-    , pUnlitColoredNoDepth = unlitColoredNoDepthWorker-    , pUnlitTextured = unlitTexturedWorker-    , pUnlitTexturedBlend = unlitTexturedBlendWorker-    , pSprite = spriteWorker-    , pSpriteOutline = spriteOutlineWorker-    , pTileMap = tileMapWorker-    , pTileMapBlend = tileMapBlendWorker-    , pWireframe = wireframeWorker-    , pWireframeNoDepth = wireframeNoDepthWorker-    , pShadowCast = shadowCastWorker+    { 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@@ -545,133 +550,144 @@   -> PipelineWorkers   -> ResourceT (Engine.StageRIO rs) PipelineObservers allocateObservers renderPasses Pipelines{..} = do-  evanwSdfExt <- External.newObserverGraphics+  -- let Settings{sMSAA} = pSettings+  initialSettings@Settings{sMSAA} <- Worker.getOutputData pSettings+  settingsExt <- Worker.newObserverIO initialSettings++  msdfExt <- External.newObserverGraphics     forward-    pMSAA-    pEvanwSdf+    sMSAA+    pMsdf +  softmaskExt <- External.newObserverGraphics+    forward+    sMSAA+    pSoftmask+   skyboxExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pSkybox    debugUVExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pDebugUV    debugTextureExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pDebugTexture    debugShadowExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pDebugShadow    depthOnlyExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pDepthOnly    litColoredExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLitColored    litColoredBlendExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLitColoredBlend    litMaterialExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLitMaterial    litMaterialBlendExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLitMaterialBlend    litTexturedExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLitTextured    litTexturedBlendExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLitTexturedBlend    line2dExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLine2d    line2dNoDepthExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pLine2dNoDepth    unlitColoredExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pUnlitColored    unlitColoredNoDepthExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pUnlitColoredNoDepth    unlitTexturedExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pUnlitTextured    unlitTexturedBlendExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pUnlitTexturedBlend    spriteExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pSprite    spriteOutlineExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pSpriteOutline    tileMapExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pTileMap    tileMapBlendExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pTileMapBlend    wireframeExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pWireframe    wireframeNoDepthExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pWireframeNoDepth    shadowCastExt <- External.newObserverGraphics     forward-    pMSAA+    sMSAA     pShadowCast    pure Pipelines-    { pEvanwSdf = evanwSdfExt+    { pSettings = settingsExt+    , pMsdf = msdfExt+    , pSoftmask = softmaskExt     , pSkybox = skyboxExt     , pDebugUV = debugUVExt     , pDebugTexture = debugTextureExt@@ -707,7 +723,8 @@   -> PipelineObservers   -> Engine.StageFrameRIO rp p fr rs () observePipelines fRenderpass workers pipelines = do-  observe @EvanwSdf.Pipeline pEvanwSdf+  observe @Msdf.Pipeline pMsdf+  observe @Msdf.Pipeline pSoftmask    observe @Skybox.Pipeline pSkybox @@ -741,12 +758,13 @@   observe @UnlitColored.Pipeline pWireframe   observe @UnlitColored.Pipeline pWireframeNoDepth +  settings <- Worker.readVar workers.pSettings   External.observeGraphics     (rpShadowPass fRenderpass) -- XXX: different RP here-    (pMSAA workers)+    settings.sMSAA     (Tagged [unTagged $ pSceneBinds pipelines])-    (pShadowCast workers)-    (pShadowCast pipelines)+    workers.pShadowCast+    pipelines.pShadowCast   where     observe       :: forall@@ -758,15 +776,17 @@          )       => (forall a . PipelinesF a -> a ^ p)       -> Engine.StageFrameRIO rps ps fr rs ()-    observe =+    observe f = do+      settings <- Worker.readVar workers.pSettings       External.observeField         @PipelinesF         @p         (rpForwardMsaa fRenderpass)-        (pMSAA workers)+        settings.sMSAA         (pSceneBinds pipelines)         workers         pipelines+        f  getSceneLayout :: PipelinesF f -> Tagged '[Scene] Vk.DescriptorSetLayout getSceneLayout = pSceneLayout@@ -779,7 +799,7 @@  stageSources :: Map Text Graphics.StageCode stageSources =-  [ ("evanw-sdf", EvanwSdf.stageCode)+  [ ("msdf", Msdf.stageCode)   , ("skybox", Skybox.stageCode)   , ("debug", Debug.stageCode)   , ("depth-only", DepthOnly.stageCode)
src/Render/Code/Lit.hs view
@@ -5,11 +5,15 @@   , structMaterial   , shadowFuns   , litMain+  , specConstants   , brdfSpecular   ) where -import Render.Code (Code(..), trimming)+import RIO +import Render.Code (Code(..), glsl, trimming)+import Render.Lit.Specialization (Specialization(..), defaultSpec)+ raySphereIntersection :: Code raySphereIntersection = Code   [trimming|@@ -171,16 +175,14 @@     if (scene.envCubeId > -1) {       irradiance = textureLod(         samplerCube(-          cubes[nonuniformEXT(scene.envCubeId)],-          samplers[0]+          cubes[nonuniformEXT(scene.envCubeId + IRRADIANCE_OFFSET)],+          samplers[IRRADIANCE_SAMPLER]         ),         normal * vec3(1, -1, 1),         IRRADIANCE_LOD       ).rgb;     } -    // BUG: raw NdotV is sometimes negative, like we're looking at the back side.-    // Cubists would appreciate, but IBL term gives that edge and the dot artifacts.     float NdotV = clamp(dot(normal, rayDir), 0.0, 1.0);      // Specular reflectance@@ -223,15 +225,25 @@     oColor = vec4(color, baseColor.a);   |] -brdfSpecular :: Code-brdfSpecular = Code-  [trimming|-    // TODO: unhardcode-    const int BRDF_LUT = 2;-    const int BRDF_LUT_SAMPLER = 3; // linear/mip0/no-repeat-    const float MAX_REFLECTION_LOD = 9.0; // todo: param/const-    const float IRRADIANCE_LOD = 10.0; // todo: param/const+specConstants :: Code+specConstants = fromString+  [glsl|+    layout(constant_id=0) const int   BRDF_LUT           = ${sBRDF_LUT};+    layout(constant_id=1) const int   BRDF_LUT_SAMPLER   = ${sBRDF_LUT_SAMPLER};+    layout(constant_id=2) const float MAX_REFLECTION_LOD = ${sMAX_REFLECTION_LOD};+    layout(constant_id=3) const float IRRADIANCE_LOD     = ${sIRRADIANCE_LOD};+    layout(constant_id=4) const int   IRRADIANCE_OFFSET  = ${sIRRADIANCE_OFFSET};+    layout(constant_id=5) const int   IRRADIANCE_SAMPLER = ${sIRRADIANCE_SAMPLER};+    layout(constant_id=6) const uint  MAX_LIGHTS         = ${sMAX_LIGHTS};+    layout(constant_id=7) const float PCF_STEP           = ${sPCF_STEP};+    layout(constant_id=8) const uint  MAX_MATERIALS      = ${sMAX_MATERIALS};+  |]+  where+    Specialization{..} = defaultSpec +brdfSpecular :: Code+brdfSpecular = fromString+  [glsl|     // Normal Distribution function --------------------------------------     float D_GGX(float dotNH, float roughness) {       float alpha = roughness * roughness;@@ -250,10 +262,6 @@     }      // Fresnel function -----------------------------------------------------    vec3 F_Schlick(float cosTheta, vec3 F0) {-      return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);-    }-     vec3 F_SchlickR(float cosTheta, vec3 F0, float roughness) {       return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);     }@@ -317,7 +325,7 @@         float rroughness = max(0.05, roughness);         float G = G_SchlicksmithGGX(dotNL, dotNV, rroughness);         // F = Fresnel factor (Reflectance depending on angle of incidence)-        vec3 F = F_Schlick(dotVH, F0);+        vec3 F = F_SchlickR(dotVH, F0, rroughness);         vec3 spec = D * F * G / (4.0 * dotNL * dotNV + 0.001);         vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);         color += (kD * ALBEDO / 3.1415926535897932384626433832795 + spec) * dotNL * lightColor;
− src/Render/Font/EvanwSdf/Code.hs
@@ -1,114 +0,0 @@-module Render.Font.EvanwSdf.Code-  ( vert-  , frag-  ) where--import RIO--import Render.Code (Code, glsl)-import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2)--vert :: Code-vert = fromString-  [glsl|-    #version 450--    ${set0binding0}--    layout(location = 0) in  vec4 iVert;-    layout(location = 1) in  vec4 iFrag;-    layout(location = 2) in  vec4 iColor;-    layout(location = 3) in  vec4 iOutlineColor;-    layout(location = 4) in ivec2 iTextureIds;-    layout(location = 5) in  vec2 iSdf;--    layout(location = 0)      out  vec2 fTexCoord;-    layout(location = 1) flat out  vec4 fColor;-    layout(location = 2) flat out  vec4 fOutlineColor;-    layout(location = 3) flat out ivec2 fTextureIds;-    layout(location = 4) flat out  vec2 fSdf;--    vec2 positions[6] = vec2[](-      vec2(-0.5, 0.5),-      vec2(0.5, 0.5),-      vec2(-0.5, -0.5),--      vec2(0.5, -0.5),-      vec2(-0.5, -0.5),-      vec2(0.5, 0.5)-    );--    vec2 texCoords[6] = vec2[](-      vec2(0.0, 0.0),-      vec2(1.0, 0.0),-      vec2(0.0, 1.0),--      vec2(1.0, 1.0),-      vec2(0.0, 1.0),-      vec2(1.0, 0.0)-    );--    void main() {-      vec2 pos = positions[gl_VertexIndex];--      gl_Position-        = scene.projection-        * scene.view-      // TODO: * iModel-        * vec4(iVert.xy + pos * iVert.zw, 0, 1.0);--      vec2 uv = texCoords[gl_VertexIndex];-      fTexCoord = iFrag.xy + uv * iFrag.zw;--      fColor = iColor;-      fOutlineColor = iOutlineColor;-      fTextureIds = iTextureIds;-      fSdf = iSdf;-    }-  |]--frag :: Code-frag = fromString-  [glsl|-    #version 450-    #extension GL_EXT_nonuniform_qualifier : enable--    ${set0binding1}-    ${set0binding2}--    layout(location = 0)      in  vec2 fTexCoord;-    layout(location = 1) flat in  vec4 fColor;-    layout(location = 2) flat in  vec4 fOutlineColor;-    layout(location = 3) flat in ivec2 fTextureIds;-    layout(location = 4) flat in  vec2 fSdf;--    layout(location = 0) out vec4 outColor;--    void main() {-      float sdf = texture(-        sampler2D(-          textures[nonuniformEXT(fTextureIds.t)],-          samplers[nonuniformEXT(fTextureIds.s)]-        ),-        fTexCoord-      ).r;--      float smoothing    = fSdf[0];-      float outlineWidth = fSdf[1];--      float outerEdgeCenter = 0.5 - outlineWidth;--      float alpha = smoothstep(-        outerEdgeCenter - smoothing,-        outerEdgeCenter + smoothing,-        sdf-      );-      float border = smoothstep(-        0.5 - smoothing,-        0.5 + smoothing,-        sdf-      );-      outColor = mix(fOutlineColor, fColor, border);-      outColor *= alpha;-    }-  |]
− src/Render/Font/EvanwSdf/Model.hs
@@ -1,47 +0,0 @@-{-# OPTIONS_GHC -fplugin Foreign.Storable.Generic.Plugin #-}--{-# LANGUAGE DeriveAnyClass #-}--module Render.Font.EvanwSdf.Model-  ( InstanceAttrs(..)-  ) where--import RIO--import Foreign.Storable.Generic (GStorable)-import Geomancy (Vec4)-import Vulkan.Core10 qualified as Vk--import Engine.Vulkan.Format (HasVkFormat(..))-import Engine.Vulkan.Pipeline.Graphics (HasVertexInputBindings(..), instanceFormat)--data InstanceAttrs = InstanceAttrs-  { vertRect     :: Vec4-  , fragRect     :: Vec4-  , color        :: Vec4-  , outlineColor :: Vec4--  , samplerId    :: Int32-  , textureId    :: Int32--  , smoothing    :: Float-  , outlineWidth :: Float-  }-  deriving (Eq, Show, Generic)---- XXX: Fine, the layout matches-instance GStorable InstanceAttrs--instance HasVkFormat InstanceAttrs where-  getVkFormat =-    [ Vk.FORMAT_R32G32B32A32_SFLOAT -- Quad scale+offset-    , Vk.FORMAT_R32G32B32A32_SFLOAT -- UV scale+offset-    , Vk.FORMAT_R32G32B32A32_SFLOAT -- Color-    , Vk.FORMAT_R32G32B32A32_SFLOAT -- Outline color--    , Vk.FORMAT_R32G32_SINT         -- Sampler + texture IDs-    , Vk.FORMAT_R32G32_SFLOAT       -- Smoothing + outline width-    ]--instance HasVertexInputBindings InstanceAttrs where-  vertexInputBindings = [instanceFormat @InstanceAttrs]
− src/Render/Font/EvanwSdf/Pipeline.hs
@@ -1,64 +0,0 @@-{-# LANGUAGE OverloadedLists #-}--module Render.Font.EvanwSdf.Pipeline-  ( Config-  , config-  , Pipeline-  , allocate-  , stageCode-  , stageSpirv-  ) where--import RIO--import Control.Monad.Trans.Resource (ResourceT)-import Data.Tagged (Tagged(..))-import Vulkan.Core10 qualified as Vk--import Engine.Vulkan.Pipeline.Graphics qualified as Graphics-import Engine.Vulkan.Types (DsLayoutBindings, HasVulkan, HasRenderPass(..))-import Render.Code (compileVert, compileFrag)-import Render.DescSets.Set0 (Scene)-import Render.Font.EvanwSdf.Code qualified as Code-import Render.Font.EvanwSdf.Model qualified as Model--type Pipeline = Graphics.Pipeline '[Scene] () Model.InstanceAttrs-type Config = Graphics.Configure Pipeline-type instance Graphics.Specialization Pipeline = ()--allocate-  :: ( HasVulkan env-     , HasRenderPass renderpass-     )-  => Vk.SampleCountFlagBits-  -> Tagged Scene DsLayoutBindings-  -> renderpass-  -> ResourceT (RIO env) Pipeline-allocate multisample tset0 = do-  fmap snd . Graphics.allocate-    Nothing-    multisample-    (config tset0)--config :: Tagged Scene DsLayoutBindings -> Config-config (Tagged set0) = Graphics.baseConfig-  { Graphics.cDescLayouts = Tagged @'[Scene] [set0]-  , Graphics.cStages      = stageSpirv-  , Graphics.cVertexInput = Graphics.vertexInput @Pipeline-  , Graphics.cDepthTest   = False-  , Graphics.cDepthWrite  = False-  , Graphics.cBlend       = True-  , Graphics.cCull        = Vk.CULL_MODE_NONE-  }--stageCode :: Graphics.StageCode-stageCode = Graphics.basicStages Code.vert Code.frag--stageSpirv :: Graphics.StageSpirv-stageSpirv = Graphics.basicStages vertSpirv fragSpirv--vertSpirv :: ByteString-vertSpirv = $(compileVert Code.vert)--fragSpirv :: ByteString-fragSpirv = $(compileFrag Code.frag)
+ src/Render/Font/Msdf/Code.hs view
@@ -0,0 +1,167 @@+module Render.Font.Msdf.Code+  ( vert+  , frag+  ) where++import RIO++import Render.Code (Code, glsl)+import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2)++vert :: Code+vert = fromString+  [glsl|+    #version 450++    ${set0binding0}++    layout(location = 0) in  vec4 iVert;+    layout(location = 1) in  vec4 iFrag;+    layout(location = 2) in ivec2 iTextureIds;+    layout(location = 3) in  vec2 iSdfDist;++    layout(location = 4) in  vec4 iColor;+    layout(location = 5) in  vec4 iOutlineColor;+    layout(location = 6) in  vec2 iSdf;++    layout(location = 0)      out  vec2 fTexCoord;+    layout(location = 1) flat out  vec4 fColor;+    layout(location = 2) flat out  vec4 fOutlineColor;+    layout(location = 3) flat out  vec4 fSdf;+    layout(location = 4) flat out ivec2 fTextureIds;++    vec2 positions[6] = vec2[](+      vec2(-0.5, 0.5),+      vec2(0.5, 0.5),+      vec2(-0.5, -0.5),++      vec2(0.5, -0.5),+      vec2(-0.5, -0.5),+      vec2(0.5, 0.5)+    );++    vec2 texCoords[6] = vec2[](+      vec2(0.0, 1),+      vec2(1.0, 1),+      vec2(0.0, 0),++      vec2(1.0, 0),+      vec2(0.0, 0),+      vec2(1.0, 1)+    );++    void main() {+      vec2 pos = positions[gl_VertexIndex];++      gl_Position+        = scene.projection+        * scene.view+      // TODO: * iModel+        * vec4(iVert.xy + pos * iVert.zw, 0, 1.0);++      vec2 uv = texCoords[gl_VertexIndex];+      fTexCoord = iFrag.xy + uv * iFrag.zw;++      fColor = iColor;+      fOutlineColor = iOutlineColor;+      fTextureIds = iTextureIds;+      fSdf = vec4(iSdfDist, iSdf);+    }+  |]++frag :: Code+frag = fromString+  [glsl|+    #version 450+    #extension GL_EXT_nonuniform_qualifier : enable++    ${set0binding1}+    ${set0binding2}++    layout(location = 0)      in  vec2 fTexCoord;+    layout(location = 1) flat in  vec4 fColor;+    layout(location = 2) flat in  vec4 fOutlineColor;+    layout(location = 3) flat in  vec4 fSdf;+    layout(location = 4) flat in ivec2 fTextureIds;++    layout(location = 0) out vec4 outColor;++    #define HARDMASK 0+    #define SOFTMASK 1+    #define SDF 2+    #define PSDF 3+    #define MSDF 4+    #define MTSDF 5++    layout(constant_id = 0) const uint type = MTSDF;+    layout(constant_id = 1) const bool srgb = false;++    float median3(float a, float b, float c) {+      return max(min(a, b), min(max(a, b), c));+    }++    void main() {+      float hard; // text+      float soft; // effects++      switch (type) {+        // single-channel+        case HARDMASK: // actually, bit-packed+        case SOFTMASK: // r8, antialiased greyscale+        case SDF: // r8, actually distance+        case PSDF: // r8, dunno+          hard = texture(+            sampler2D(+              textures[nonuniformEXT(fTextureIds[1])],+              samplers[nonuniformEXT(fTextureIds[0])]+            ),+            fTexCoord+          ).r;+          if (srgb) hard = pow(hard, 1/2.2);+          // No outlines for non-SDF types, just apply tint+          outColor = fColor * vec4(hard);+          return;+        // 3-channel+        case MSDF:+          vec3 msdf = texture(+            sampler2D(+              textures[nonuniformEXT(fTextureIds[1])],+              samplers[nonuniformEXT(fTextureIds[0])]+            ),+            fTexCoord+          ).rgb;+          hard = median3(msdf.r, msdf.g, msdf.b);+          if (srgb) hard = pow(hard, 1/2.2);+          soft = hard; // no dedicated channel+          break;+        // 4-channel+        case MTSDF:+          vec4 mtsdf = texture(+            sampler2D(+              textures[nonuniformEXT(fTextureIds[1])],+              samplers[nonuniformEXT(fTextureIds[0])]+            ),+            fTexCoord+          ).rgba;+          hard = median3(mtsdf.r, mtsdf.g, mtsdf.b);+          if (srgb) hard = pow(hard, 1/2.2);+          soft = mtsdf.a; // dedicated channel for soft effects+          break;+      }++      float sdfLower = fSdf[0];+      float sdfRange = fSdf[1];+      float sdfSmoothing = fSdf[2]; // fallback+      float sdfOutline = fSdf[3];++      float hardPx = sdfLower + (hard * sdfRange);+      float hardWidth = max(fwidth(hard) * sdfRange, sdfSmoothing);+      float hardCoverage = smoothstep(-0.5, 0.5, hardPx / hardWidth);++     	float softPx = sdfLower + (soft * sdfRange);+     	float softWidth = max(fwidth(soft) * sdfRange, sdfSmoothing);+     	float softCoverage = clamp(smoothstep(-sdfOutline / softWidth, 0, softPx / softWidth), 0, 1);++     	outColor = mix(fOutlineColor, fColor, hardCoverage) * softCoverage;+    }+  |]
+ src/Render/Font/Msdf/Model.hs view
@@ -0,0 +1,48 @@+{-# OPTIONS_GHC -fplugin Foreign.Storable.Generic.Plugin #-}++{-# LANGUAGE DeriveAnyClass #-}++module Render.Font.Msdf.Model+  ( InstanceAttrs(..)+  ) where++import RIO++import Geomancy (IVec2, Vec2, Vec4)+import Graphics.Gl.Block (Block)+import Graphics.Gl.Block qualified as Block+import Vulkan.Core10 qualified as Vk++import Engine.Vulkan.Format (HasVkFormat(..))+import Engine.Vulkan.Pipeline.Graphics (HasVertexInputBindings(..), instanceFormat)++data InstanceAttrs = InstanceAttrs+  { -- static, can be shoved into font buffer+    vertRect     :: Vec4+  , fragRect     :: Vec4+  , textureIds    :: IVec2 -- | sampler + textureId+  , sdfDist :: Vec2++    -- dynamic+  , color        :: Vec4+  , outlineColor :: Vec4+  , smoothing    :: Float -- size-related+  , outlineWidth :: Float+  }+  deriving (Eq, Show, Generic, Block)+  deriving Storable via (Block.Packed InstanceAttrs)++instance HasVkFormat InstanceAttrs where+  getVkFormat =+    [ Vk.FORMAT_R32G32B32A32_SFLOAT -- Quad scale+offset+    , Vk.FORMAT_R32G32B32A32_SFLOAT -- UV scale+offset+    , Vk.FORMAT_R32G32_SINT         -- Sampler + texture IDs+    , Vk.FORMAT_R32G32_SFLOAT -- SDF lower + range+    -----+    , Vk.FORMAT_R32G32B32A32_SFLOAT -- Color+    , Vk.FORMAT_R32G32B32A32_SFLOAT -- Outline color+    , Vk.FORMAT_R32G32_SFLOAT -- Smoothing + outline+    ]++instance HasVertexInputBindings InstanceAttrs where+  vertexInputBindings = [instanceFormat @InstanceAttrs]
+ src/Render/Font/Msdf/Pipeline.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE OverloadedLists #-}++module Render.Font.Msdf.Pipeline+  ( Config+  , config+  , Pipeline+  , allocate+  , Specialization(..)+  , Mode(..)+  , defaultSpecialization+  , stageCode+  , stageSpirv+  ) where++import RIO++import Control.Monad.Trans.Resource (ResourceT)+import Data.Tagged (Tagged(..))+import Vulkan.Core10 qualified as Vk++import Engine.Vulkan.Pipeline.Graphics qualified as Graphics+import Engine.Vulkan.Shader qualified as Shader+import Engine.Vulkan.Types (DsLayoutBindings, HasVulkan, HasRenderPass(..))+import Render.Code (compileVert, compileFrag)+import Render.DescSets.Set0 (Scene)+import Render.Font.Msdf.Code qualified as Code+import Render.Font.Msdf.Model qualified as Model++type Pipeline = Graphics.Pipeline '[Scene] () Model.InstanceAttrs+type Config = Graphics.Configure Pipeline+type instance Graphics.Specialization Pipeline = Specialization++data Specialization = Specialization+  { mode :: Mode+  , srgb :: Bool+  } deriving (Eq, Ord, Show)++defaultSpecialization :: Specialization+defaultSpecialization = Specialization+  { mode = MTSDF+  , srgb = False+  }++data Mode+  = HardMask+  | SoftMask+  | SDF+  | PSDF+  | MSDF+  | MTSDF+  deriving (Eq, Ord, Show, Enum, Bounded)++instance Shader.Specialization Specialization where+  specializationData Specialization{..} =+    [ fromIntegral (fromEnum mode)+    , Shader.packConstData srgb+    ]++allocate+  :: ( HasVulkan env+     , HasRenderPass renderpass+     )+  => Specialization+  -> Vk.SampleCountFlagBits+  -> Tagged Scene DsLayoutBindings+  -> renderpass+  -> ResourceT (RIO env) Pipeline+allocate spec multisample tset0 = do+  fmap snd . Graphics.allocate+    Nothing+    multisample+    (config spec tset0)++config :: Specialization -> Tagged Scene DsLayoutBindings -> Config+config spec (Tagged set0) = Graphics.baseConfig+  { Graphics.cDescLayouts = Tagged @'[Scene] [set0]+  , Graphics.cStages      = stageSpirv+  , Graphics.cVertexInput = Graphics.vertexInput @Pipeline+  , Graphics.cDepthTest   = False+  , Graphics.cDepthWrite  = False+  , Graphics.cBlend       = True+  , Graphics.cCull        = Vk.CULL_MODE_NONE+  , Graphics.cSpecialization = spec+  }++stageCode :: Graphics.StageCode+stageCode = Graphics.basicStages Code.vert Code.frag++stageSpirv :: Graphics.StageSpirv+stageSpirv = Graphics.basicStages vertSpirv fragSpirv++vertSpirv :: ByteString+vertSpirv = $(compileVert Code.vert)++fragSpirv :: ByteString+fragSpirv = $(compileFrag Code.frag)
src/Render/Lit/Colored/Code.hs view
@@ -6,7 +6,7 @@ import RIO  import Render.Code (Code, glsl)-import Render.Code.Lit (litMain, shadowFuns, structLight, brdfSpecular)+import Render.Code.Lit (brdfSpecular, litMain, shadowFuns, specConstants, structLight) import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2, set0binding3, set0binding4, set0binding5)  vert :: Code@@ -58,9 +58,7 @@      layout(early_fragment_tests) in; -    // TODO: move to spec constant-    const uint MAX_LIGHTS = 255;-    const float PCF_STEP = 1.5 / 4096;+    ${specConstants}      // TODO: move to material     const float reflectivity = 1.0/256.0;
src/Render/Lit/Colored/Pipeline.hs view
@@ -23,24 +23,26 @@ import Render.DescSets.Set0 (Scene) import Render.Lit.Colored.Code qualified as Code import Render.Lit.Colored.Model qualified as Model+import Render.Lit.Specialization (Specialization)  type Pipeline = Graphics.Pipeline '[Scene] Model.Vertex Model.InstanceAttrs type Config = Graphics.Configure Pipeline-type instance Graphics.Specialization Pipeline = ()+type instance Graphics.Specialization Pipeline = Specialization  allocate   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Specialization+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsLayoutBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocate multisample tset0 rp = do+allocate spec multisample tset0 rp = do   (_, p) <- Graphics.allocate     Nothing     multisample-    (config tset0)+    (config spec tset0)     rp   pure p @@ -48,29 +50,31 @@   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Specialization+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsLayoutBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocateBlend multisample tset0 rp = do+allocateBlend spec multisample tset0 rp = do   (_, p) <- Graphics.allocate     Nothing     multisample-    (configBlend tset0)+    (configBlend spec tset0)     rp   pure p -config :: Tagged Scene DsLayoutBindings -> Config-config (Tagged set0) = Graphics.baseConfig-  { Graphics.cDescLayouts  = Tagged @'[Scene] [set0]-  , Graphics.cStages       = stageSpirv-  , Graphics.cVertexInput  = Graphics.vertexInput @Pipeline-  , Graphics.cDepthWrite   = False -- XXX: Lit pipelines require depth pre-pass-  , Graphics.cDepthCompare = Vk.COMPARE_OP_EQUAL+config :: Specialization -> Tagged Scene DsLayoutBindings -> Config+config spec (Tagged set0) = Graphics.baseConfig+  { Graphics.cDescLayouts    = Tagged @'[Scene] [set0]+  , Graphics.cStages         = stageSpirv+  , Graphics.cVertexInput    = Graphics.vertexInput @Pipeline+  , Graphics.cDepthWrite     = False -- XXX: Lit pipelines require depth pre-pass+  , Graphics.cDepthCompare   = Vk.COMPARE_OP_EQUAL+  , Graphics.cSpecialization = spec   } -configBlend :: Tagged Scene DsLayoutBindings -> Config-configBlend tset0 = (config tset0)+configBlend :: Specialization -> Tagged Scene DsLayoutBindings -> Config+configBlend spec tset0 = (config spec tset0)   { Graphics.cBlend = True   , Graphics.cDepthCompare =       -- XXX: blended objects aren't a part of depth prepass
src/Render/Lit/Material/Code.hs view
@@ -6,7 +6,7 @@ import RIO  import Render.Code (Code, glsl)-import Render.Code.Lit (litMain, shadowFuns, structLight, structMaterial, brdfSpecular)+import Render.Code.Lit (brdfSpecular, litMain, shadowFuns, specConstants, structLight, structMaterial) import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2, set0binding3, set0binding4, set0binding5, set0binding6)  vert :: Code@@ -64,12 +64,7 @@      layout(early_fragment_tests) in; -    // XXX: copypasta from Lit.Colored-    // TODO: move to spec constant-    const uint MAX_LIGHTS = 255;-    const float PCF_STEP = 1.5 / 4096;--    const uint MAX_MATERIALS = 2048;+    ${specConstants}      ${structLight}     ${structMaterial}@@ -127,7 +122,7 @@         ).rgb;         // XXX: assuming sRGB textures, even for AMR         packed = pow(packed, vec3(1.0/2.2));-        nonOcclusion -= packed.r;+        nonOcclusion *= packed.r;         metallic *= packed.b;         roughness *= packed.g;       }
src/Render/Lit/Material/Pipeline.hs view
@@ -23,24 +23,26 @@ import Render.DescSets.Set0 (Scene) import Render.Lit.Material.Code qualified as Code import Render.Lit.Material.Model qualified as Model+import Render.Lit.Specialization (Specialization)  type Pipeline = Graphics.Pipeline '[Scene] Model.Vertex Model.InstanceAttrs type Config = Graphics.Configure Pipeline-type instance Graphics.Specialization Pipeline = ()+type instance Graphics.Specialization Pipeline = Specialization  allocate   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Specialization+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsLayoutBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocate multisample tset0 rp = do+allocate spec multisample tset0 rp = do   (_, p) <- Graphics.allocate     Nothing     multisample-    (config tset0)+    (config spec tset0)     rp   pure p @@ -48,29 +50,31 @@   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Specialization+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsLayoutBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocateBlend multisample tset0 rp = do+allocateBlend spec multisample tset0 rp = do   (_, p) <- Graphics.allocate     Nothing     multisample-    (configBlend tset0)+    (configBlend spec tset0)     rp   pure p -config :: Tagged Scene DsLayoutBindings -> Config-config (Tagged set0) = Graphics.baseConfig-  { Graphics.cDescLayouts  = Tagged @'[Scene] [set0]-  , Graphics.cStages       = stageSpirv-  , Graphics.cVertexInput  = Graphics.vertexInput @Pipeline-  , Graphics.cDepthWrite   = False -- XXX: Lit pipelines require depth pre-pass-  , Graphics.cDepthCompare = Vk.COMPARE_OP_EQUAL+config :: Specialization -> Tagged Scene DsLayoutBindings -> Config+config spec (Tagged set0) = Graphics.baseConfig+  { Graphics.cDescLayouts    = Tagged @'[Scene] [set0]+  , Graphics.cStages         = stageSpirv+  , Graphics.cVertexInput    = Graphics.vertexInput @Pipeline+  , Graphics.cDepthWrite     = False -- XXX: Lit pipelines require depth pre-pass+  , Graphics.cDepthCompare   = Vk.COMPARE_OP_EQUAL+  , Graphics.cSpecialization = spec   } -configBlend :: Tagged Scene DsLayoutBindings -> Config-configBlend tset0 = (config tset0)+configBlend :: Specialization -> Tagged Scene DsLayoutBindings -> Config+configBlend spec tset0 = (config spec tset0)   { Graphics.cBlend =       True   , Graphics.cDepthCompare =
+ src/Render/Lit/Specialization.hs view
@@ -0,0 +1,51 @@+module Render.Lit.Specialization+  ( Specialization(..)+  , defaultSpec+  ) where++import RIO++import Engine.Vulkan.Shader qualified as Shader++import Global.Resource.Texture.Base qualified as Base+import Render.Samplers qualified as Samplers++defaultSpec :: Specialization+defaultSpec = Specialization+  { sBRDF_LUT           = Base.indices.ibl_brdf_lut+  , sBRDF_LUT_SAMPLER   = Samplers.indices.linear+  , sMAX_REFLECTION_LOD = 9.0+  , sIRRADIANCE_LOD     = 10.0+  , sIRRADIANCE_OFFSET  = 0+  , sIRRADIANCE_SAMPLER = Samplers.indices.linearMipRepeat+  , sMAX_LIGHTS         = 255+  , sPCF_STEP           = 1.5 / 4096+  , sMAX_MATERIALS      = 2048+  }++data Specialization = Specialization+  { sBRDF_LUT           :: Int32+  , sBRDF_LUT_SAMPLER   :: Int32+  , sMAX_REFLECTION_LOD :: Float+  , sIRRADIANCE_LOD     :: Float+  , sIRRADIANCE_OFFSET  :: Int32+  , sIRRADIANCE_SAMPLER :: Int32+  , sMAX_LIGHTS         :: Word32+  , sPCF_STEP           :: Float+  , sMAX_MATERIALS      :: Word32+  }+  deriving (Eq, Ord, Show, Generic)++instance Shader.Specialization Specialization where+  {-# INLINE specializationData #-}+  specializationData Specialization{..} =+    [ Shader.packConstData sBRDF_LUT+    , Shader.packConstData sBRDF_LUT_SAMPLER+    , Shader.packConstData sMAX_REFLECTION_LOD+    , Shader.packConstData sIRRADIANCE_LOD+    , Shader.packConstData sIRRADIANCE_OFFSET+    , Shader.packConstData sIRRADIANCE_SAMPLER+    , Shader.packConstData sMAX_LIGHTS+    , Shader.packConstData sPCF_STEP+    , Shader.packConstData sMAX_MATERIALS+    ]
src/Render/Lit/Textured/Code.hs view
@@ -6,7 +6,7 @@ import RIO  import Render.Code (Code, glsl)-import Render.Code.Lit (litMain, shadowFuns, structLight, brdfSpecular)+import Render.Code.Lit (brdfSpecular, litMain, shadowFuns, specConstants, structLight) import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2, set0binding3, set0binding4, set0binding5)  vert :: Code@@ -61,10 +61,7 @@      layout(early_fragment_tests) in; -    // XXX: copypasta from Lit.Colored-    // TODO: move to spec constant-    const uint MAX_LIGHTS = 255;-    const float PCF_STEP = 1.5 / 4096;+    ${specConstants}      // TODO: move to material     const float reflectivity = 1.0/256.0;
src/Render/Lit/Textured/Pipeline.hs view
@@ -23,24 +23,26 @@ import Render.DescSets.Set0 (Scene) import Render.Lit.Textured.Code qualified as Code import Render.Lit.Textured.Model qualified as Model+import Render.Lit.Specialization (Specialization)  type Pipeline = Graphics.Pipeline '[Scene] Model.Vertex Model.Attrs type Config = Graphics.Configure Pipeline-type instance Graphics.Specialization Pipeline = ()+type instance Graphics.Specialization Pipeline = Specialization  allocate   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Specialization+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsLayoutBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocate multisample tset0 rp = do+allocate spec multisample tset0 rp = do   (_, p) <- Graphics.allocate     Nothing     multisample-    (config tset0)+    (config spec tset0)     rp   pure p @@ -48,29 +50,31 @@   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Specialization+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsLayoutBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocateBlend multisample tset0 rp = do+allocateBlend spec multisample tset0 rp = do   (_, p) <- Graphics.allocate     Nothing     multisample-    (configBlend tset0)+    (configBlend spec tset0)     rp   pure p -config :: Tagged Scene DsLayoutBindings -> Config-config (Tagged set0) = Graphics.baseConfig-  { Graphics.cDescLayouts  = Tagged @'[Scene] [set0]-  , Graphics.cStages       = stageSpirv-  , Graphics.cVertexInput  = Graphics.vertexInput @Pipeline-  , Graphics.cDepthWrite   = False -- XXX: Lit pipelines require depth pre-pass-  , Graphics.cDepthCompare = Vk.COMPARE_OP_EQUAL+config :: Specialization -> Tagged Scene DsLayoutBindings -> Config+config spec (Tagged set0) = Graphics.baseConfig+  { Graphics.cDescLayouts    = Tagged @'[Scene] [set0]+  , Graphics.cStages         = stageSpirv+  , Graphics.cVertexInput    = Graphics.vertexInput @Pipeline+  , Graphics.cDepthWrite     = False -- XXX: Lit pipelines require depth pre-pass+  , Graphics.cDepthCompare   = Vk.COMPARE_OP_EQUAL+  , Graphics.cSpecialization = spec   } -configBlend :: Tagged Scene DsLayoutBindings -> Config-configBlend tset0 = (config tset0)+configBlend :: Specialization -> Tagged Scene DsLayoutBindings -> Config+configBlend spec tset0 = (config spec tset0)   { Graphics.cBlend = True   , Graphics.cDepthCompare =       -- XXX: blended objects aren't a part of depth prepass
src/Render/ShadowMap/RenderPass.hs view
@@ -27,8 +27,11 @@ import Resource.Region qualified as Region import Resource.Vulkan.Named qualified as Named --- * Depth-only pass for shadowmapping pipelines+{- | Depth-only pass for multi-view shadowmapping pipelines +Each light writes to its own layer of the same size.+Instantiate multiple times for different sizes.+-} data ShadowMap = ShadowMap   { smRenderPass  :: Vk.RenderPass   , smDepthImage  :: AllocatedImage
src/Render/Unlit/Line2d/Model.hs view
@@ -26,7 +26,6 @@ import RIO  import Control.Monad.Trans.Resource qualified as Resource-import Data.Type.Equality (type (~)) import Data.Vector.Generic qualified as Generic import Foreign.Storable.Generic (GStorable) import Geomancy (Vec2, Vec4, vec3)
src/Resource/Font.hs view
@@ -1,6 +1,5 @@ module Resource.Font-  ( Config(..)-  , collectionTextures+  ( collectionTextures    , Font(..)   , allocate@@ -14,26 +13,20 @@ import Vulkan.Core10 qualified as Vk  import Engine.Vulkan.Types (MonadVulkan, Queues)-import Resource.Font.EvanW qualified as EvanW+import Resource.Font.Ktxf qualified as Ktxf import Resource.Source (Source) import Resource.Texture (Texture, Flat) import Resource.Texture.Ktx2 qualified as Ktx2  -- * General collection tools -data Config = Config-  { configContainer :: Source-  , configTexture   :: Source-  }-  deriving (Show)- collectionTextures :: Foldable collection => collection Font -> Vector (Texture Flat) collectionTextures = Vector.fromList . map texture . toList  -- * Individual fonts  data Font = Font-  { container :: EvanW.Container+  { bundle :: Ktxf.Bundle   , texture   :: Texture Flat   } @@ -45,13 +38,9 @@      , Resource.MonadResource m      )   => Queues Vk.CommandPool-  -> Config+  -> Source   -> m Font-allocate pools Config{..} = do-  container <- withFrozenCallStack $-    EvanW.load configContainer--  texture <- withFrozenCallStack $-    Ktx2.load pools configTexture-+allocate pools source = do+  bundle <- withFrozenCallStack $ Ktxf.load source+  texture <- withFrozenCallStack $ Ktx2.load pools source   pure Font{..}
− src/Resource/Font/EvanW.hs
@@ -1,175 +0,0 @@-{- |-  JSON font loader for bitmaps and SDFs--  Generator: https://evanw.github.io/font-texture-generator/--  Usage (WebGL): https://evanw.github.io/font-texture-generator/example-webgl/--}--module Resource.Font.EvanW-  ( load-  , Container(..)-  , Character(..)--  , putLine-  , PutChar(..)-  ) where--import RIO--import Data.Aeson (FromJSON, eitherDecodeStrict')-import Foreign qualified-import Geomancy (Vec2, vec2, pattern WithVec2)-import Geomancy.Layout qualified as Layout-import Geomancy.Layout.Alignment (Alignment)-import Geomancy.Layout.Box (Box(..))-import Geomancy.Layout.Box qualified as Box-import GHC.Stack (withFrozenCallStack)-import RIO.HashMap qualified as HashMap-import RIO.Text qualified as Text-import Vulkan.NamedType ((:::))--import Resource.Source (Source)-import Resource.Source qualified as Source---- * Loading--newtype FontError = FontError Text-  deriving (Eq, Ord, Show, Generic)--instance Exception FontError--data Container = Container-  { name       :: Text-  , size       :: Float-  , bold       :: Bool-  , italic     :: Bool-  , width      :: Float-  , height     :: Float-  , characters :: HashMap Char Character-  }-  deriving (Eq, Ord, Show, Generic)--data Character = Character-  { x       :: Float-  , y       :: Float-  , width   :: Float-  , height  :: Float-  , originX :: Float-  , originY :: Float-  , advance :: Float-  }-  deriving (Eq, Ord, Show, Generic)--instance FromJSON Container-instance FromJSON Character--load-  :: ( MonadIO m-     , MonadReader env m-     , HasLogFunc env-     , HasCallStack-     )-  => Source -> m Container-load =-  withFrozenCallStack $-    Source.load \bytes ->-      case eitherDecodeStrict' bytes of-        Left err ->-          liftIO . throwIO . FontError $ Text.pack err-        Right res ->-          pure res---- * Typesetting--data PutChar = PutChar-  { pcPos    :: Vec2-  , pcSize   :: Vec2-  , pcOffset :: Vec2-  , pcScale  :: Vec2-  } deriving (Show)--instance Foreign.Storable PutChar where-  alignment ~_ = 16--  sizeOf ~_ = 32 -- 4 of pairs of floats--  peek ptr = PutChar-    <$> Foreign.peekElemOff (Foreign.castPtr ptr) 0-    <*> Foreign.peekElemOff (Foreign.castPtr ptr) 1-    <*> Foreign.peekElemOff (Foreign.castPtr ptr) 2-    <*> Foreign.peekElemOff (Foreign.castPtr ptr) 3--  poke ptr PutChar{..} = do-    Foreign.pokeElemOff (Foreign.castPtr ptr) 0 pcPos-    Foreign.pokeElemOff (Foreign.castPtr ptr) 1 pcSize-    Foreign.pokeElemOff (Foreign.castPtr ptr) 2 pcOffset-    Foreign.pokeElemOff (Foreign.castPtr ptr) 3 pcScale--putLine-  :: "WH"        ::: Vec2-  -> "XY"        ::: Vec2-  -> "Alignment" ::: Alignment-  -> "Size"      ::: Float-  -> "Font"      ::: Container-  -> "Line"      ::: [Char]-  -> ("scale" ::: Float, [PutChar])-putLine bs bp a targetSize font =-  (sizeScale,) . extract . foldl' step (0, [])-  where-    parent = Box-      { position = bp-      , size = bs-      }--    Container-      { size   = fontSize-      , width  = atlasWidth-      , height = atlasHeight-      , characters-      } = font--    sizeScale = targetSize / fontSize-    baseline = 0.7125--    extract (offX, bits) =-      Box.withTRBL (Layout.placeSize a (vec2 (offX * sizeScale) targetSize) parent) \t _r _b l -> do-        (WithVec2 w h, WithVec2 x y, (pcOffset, pcScale)) <- bits-        let-          pcPos =-            vec2-              (l + x * sizeScale)-              (t + y * sizeScale + baseline * targetSize)--          pcSize =-            vec2-              (w * sizeScale)-              (h * sizeScale)--        pure PutChar{..}--    step (offX, acc) = \case-      ' ' ->-        ( offX + fontSize / 2-        , acc-        )--      char ->-        case HashMap.lookup char characters of-          Nothing ->-            ( offX-            , acc-            )-          Just Character{..} ->-            ( offX + advance-            , ( vec2 width (-height)-              , vec2 ox oy-              , (uvOffset, uvScale)-              ) : acc-            )-            where-              ox = width / 2 - originX + offX-              oy = height / 2 - originY--              uvOffset = vec2 (x / atlasWidth) (y / atlasHeight)-              uvScale  = vec2 (width / atlasWidth) (height / atlasHeight)
+ src/Resource/Font/Ktxf.hs view
@@ -0,0 +1,141 @@+module Resource.Font.Ktxf+  ( Font.Bundle(..)+  , load++  , Stack(..)+  , allocateMonoStacks+  , allocateStack1++  , shape+  , Layout.ShapedText+  , place+  , Layout.Strategy(..)+  , Layout.LineEnd(..)+  , PutChar(..)+  ) where++import RIO++import Geomancy (Vec2, vec2, withVec2)+import Geomancy.Layout.Alignment (Alignment(..))+import Geomancy.Layout.Box (Box(..))+import UnliftIO.Resource (MonadResource)+import UnliftIO.Resource qualified as Resource++import Resource.Source (Source)+import Resource.Source qualified as Source++import Codec.Ktx2.Font (Bundle(..), loadBundleFile, loadBundleBytes)+import Codec.Ktx2.Font qualified as Font+import Codec.Ktx2.Font.Layout qualified as Layout+import Codec.Ktx2.Font.Shaping qualified as Shaping+import Graphics.MSDF.Atlas.Compact qualified as Atlas++-- * Loading++newtype FontError = FontError Text+  deriving (Eq, Ord, Show, Generic)++instance Exception FontError++load+  :: ( MonadIO m+     , MonadReader env m+     , HasLogFunc env+     , HasCallStack+     )+  => Source -> m Bundle+load = \case+  Source.File _label path ->+    liftIO $ loadBundleFile path+  embedded ->+    Source.load (liftIO . loadBundleBytes) embedded++-- | A shaping context for one bundle, annotated with its texture id.+data Stack = Stack+  { context :: Font.StackContext Int32+  , style   :: Layout.TextStyle+  }++allocateMonoStacks :: (MonadResource m, Traversable t) => t (Int32, Font.Bundle) -> m (t Stack)+allocateMonoStacks = traverse (uncurry allocateStack1)++allocateStack1 :: MonadResource m => Int32 -> Font.Bundle -> m Stack+allocateStack1 textureId bundle = do+  stack <- liftIO $ Font.createStackContext (Identity bundle)+  _key <- Resource.register $ Font.destroyStackContext stack+  let context = Font.mapWithBundle (Identity (bundle, const textureId)) stack+  style <- liftIO $ Layout.bundleStyle context bundle+  pure Stack{context, style}++-- * Typesetting++-- BUG: pipeline-specific, should be moved to pipeline models++-- XXX: those can be collapsed to just an index into glyph storage (and the boxes are already storable)+data PutChar = PutChar+  { pcPos    :: Vec2 -- quads, static base + dynamic cursor'd positions in ems * shared dynamic scale+  , pcSize   :: Vec2 -- static base * shared dynamic scale+  , pcOffset :: Vec2 -- uv, static+  , pcScale  :: Vec2+  , pcDistanceLower :: Float+  , pcDistanceRange :: Float+  , pcTextureId :: Int32+  } deriving (Show)++-- | Shape and measure the text once, so it can be placed into any box.+shape :: Stack -> Text -> IO Layout.ShapedText+shape Stack{context, style} = Layout.shapeText context style++place+  :: Box -- ^ Parent box, screen units+  -> Alignment -- ^ Line alignment inside the box, block alignment along it+  -> Float -- ^ Line height, in cap-height units+  -> Layout.Strategy+  -> Float -- ^ Target size, screen units per cap height+  -> Stack+  -> Layout.ShapedText+  -> [PutChar]+place parent (Alignment alignment) lineHeight strategy targetSize Stack{context} shaped =+  withVec2 alignment \alignX alignY ->+  withVec2 parent.size \parentWidth parentHeight -> do+    let+      maxWidth = parentWidth / targetSize++      placed = Layout.placeText+        Layout.LayoutOptions+          { cursor = Shaping.initialCursorDown lineHeight+          , strategy+          , align = Layout.AlignLeft+          }+        maxWidth+        shaped++      -- XXX: ignoring glyph height and using cap height (the sizes are normalized to it)+      textHeight = 1 + (fromIntegral (length placed) - 1) * lineHeight++      blockOffset =+        parent.position+          - parent.size * 0.5+          + vec2 0 ((parentHeight - textHeight * targetSize) * alignY + targetSize)++    line <- placed+    let lineOffset = vec2 ((maxWidth - line.width) * alignX * targetSize) 0++    ((font, atlas_), glyphs) <- line.runs+    (pcTextureId, atlas) <- maybeToList $ (,) <$> Font.lookupBundled font context <*> atlas_+    Shaping.PlacedGlyph{glyph = atlasBox, plane = quadBox} <- glyphs+    guard $ quadBox.w > 0 && quadBox.h > 0+    let+      Atlas.Box{x=ax,y=ay,w=aw,h=ah} = atlasBox+      pcOffset = vec2 ax ay+      pcScale = vec2 aw ah+    let+      Atlas.Box{x,y,w,h} = quadBox+      pcPos = vec2 (x * targetSize) (-y * targetSize) + blockOffset + lineOffset+      pcSize = vec2 (w * targetSize) (h * targetSize)+      Atlas.Compact+        { _distanceLower = pcDistanceLower+        , _distanceRange = pcDistanceRange+        } = atlas+    pure PutChar{..}
src/Stage/Loader/Render.hs view
@@ -52,5 +52,5 @@         Draw.indexed cb (UI.quadUV ui) background         Draw.indexed cb (UI.quadUV ui) spinner -      Graphics.bind cb pEvanwSdf $+      Graphics.bind cb pMsdf $         traverse_ (Draw.quads cb) uiMessages
src/Stage/Loader/Scene.hs view
@@ -5,7 +5,6 @@  import RIO -import Data.Type.Equality (type (~)) import UnliftIO.Resource (MonadResource)  import Engine.Camera qualified as Camera
src/Stage/Loader/Setup.hs view
@@ -18,6 +18,7 @@ import Engine.Vulkan.Types (Queues) import Engine.Worker qualified as Worker import Geometry.Quad qualified as Quad+import RIO.State (gets) import Render.Basic qualified as Basic import Render.DescSets.Set0 qualified as Set0 import Render.Samplers qualified as Samplers@@ -25,12 +26,11 @@ import Resource.Combined.Textures qualified as CombinedTextures import Resource.CommandBuffer (withPools) import Resource.Font qualified as Font+import Resource.Font.Ktxf qualified as Ktxf import Resource.Model qualified as Model import Resource.Region qualified as Region import Resource.Source (Source) import Resource.Texture.Ktx2 qualified as Ktx2-import RIO.State (gets)-import RIO.Vector.Partial ((!)) import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk @@ -41,92 +41,88 @@  bootstrap   :: Text-  -> (Font.Config, Font.Config)   -> (Source, Source)+  -> (Source, Source)   -> ((Text -> StageSetupRIO ()) -> StageSetupRIO loaded)   -> (loaded -> StackStage)-  -> ( Setup Vector Vector loaded -> Engine.StackStage-     , Engine.StageSetupRIO (Setup Vector Vector loaded)+  -> ( Setup loaded -> Engine.StackStage+     , Engine.StageSetupRIO (Setup loaded)      )-bootstrap titleMessage (smallFont, largeFont) (bgPath, spinnerPath) loadAction nextStage =+bootstrap titleMessage (smallFontSrc, largeFontSrc) (bgPath, spinnerPath) loadAction nextStage =   (stackStageBootstrap, action)   where     action = withPools \pools -> do       logDebug "Bootstrapping loader" -      fonts <- traverse (Font.allocate pools) [smallFont, largeFont]+      smallFont <- Font.allocate pools smallFontSrc+      largeFont <- Font.allocate pools largeFontSrc+       textures <- traverse (Ktx2.load pools) [bgPath, spinnerPath]        let-        fontContainers = fmap Font.container fonts         combinedTextures = Collection.enumerate CombinedTextures.Collection           { textures = textures-          , fonts    = fmap Font.texture fonts+          , fonts    = [smallFont.texture, largeFont.texture]           }        let+        (backgroundIx, spinnerIx, smallFontId, largeFontId) =+          case map fst $ toList combinedTextures of+            [a, b, c, d] -> (a, b, c, d)+            _ -> error "assert: combinedTextures has exactly 4 elements"++      smallFontStack <- Ktxf.allocateStack1 smallFontId smallFont.bundle+      largeFontStack <- Ktxf.allocateStack1 largeFontId largeFont.bundle++      let         uiSettings = UI.Settings           { titleMessage = titleMessage-          , backgroundIx = 0-          , spinnerIx    = 1-+          , backgroundIx = backgroundIx+          , spinnerIx    = spinnerIx           , combined = combinedTextures-          , fonts    = fontContainers--          , smallFont = \fs -> fs ! 0-          , largeFont = \fs -> fs ! 1+          , smallFont = smallFontStack+          , largeFont = largeFontStack+          , fontBundles = [smallFont.bundle, largeFont.bundle]           }        logDebug "Finished bootstrapping loader"       pure Setup{..} -data Setup fonts textures loaded = Setup+data Setup loaded = Setup   { loadAction :: (Text -> StageSetupRIO ()) -> StageSetupRIO loaded   , nextStage  :: loaded -> StackStage-  , uiSettings :: UI.Settings textures fonts+  , uiSettings :: UI.Settings   } -stackStageBootstrap-  :: (Traversable fonts, Traversable textures)-  => Setup fonts textures loaded -> StackStage+stackStageBootstrap :: Setup loaded -> StackStage stackStageBootstrap Setup{..} = stackStage loadAction nextStage uiSettings  stackStage-  :: (Traversable fonts, Traversable textures)-  => ((Text -> StageSetupRIO ()) -> StageSetupRIO loaded)+  :: ((Text -> StageSetupRIO ()) -> StageSetupRIO loaded)   -> (loaded -> StackStage)-  -> UI.Settings textures fonts+  -> UI.Settings   -> StackStage stackStage loadAction nextStage uiSettings =   StackStage $ loaderStage loadAction nextStage uiSettings  loaderStage-  :: (Traversable fonts, Traversable textures)-  => ((Text -> StageSetupRIO ()) -> StageSetupRIO loaded)+  :: ((Text -> StageSetupRIO ()) -> StageSetupRIO loaded)   -> (loaded -> StackStage)-  -> UI.Settings textures fonts+  -> UI.Settings   -> Basic.Stage FrameResources RunState loaderStage loadAction nextStage uiSettings = Stage.assemble "Loader" rendering resources (Just scene)   where     rendering = Stage.Rendering-      { rAllocateRP = allocateRenderPass+      { rAllocateRP = Basic.allocate_       , rAllocateP = allocatePipelines       } -    allocateRenderPass swapchain = do-      Basic.allocate-        Basic.Settings-          { sShadowLayers = 1-          , sShadowSize   = 1-          }-        swapchain-     allocatePipelines swapchain rps = do       logDebug "Allocating loader pipelines"       samplers <- Samplers.allocate (Swapchain.getAnisotropy swapchain)       Basic.allocatePipelines+        (Basic.swapchainSettings swapchain)         (sceneBinds samplers)-        (Swapchain.getMultisample swapchain)         rps      sceneBinds samplers = Set0.mkBindings@@ -149,7 +145,7 @@ initialRunState   :: ((Text -> StageSetupRIO ()) -> StageSetupRIO loaded)   -> (loaded -> StackStage)-  -> UI.Settings textures fonts+  -> UI.Settings   -> StageSetupRIO (Resource.ReleaseKey, RunState) initialRunState loadAction nextStage uiSettings =   withPools \pools -> Region.run do@@ -199,11 +195,13 @@     -- XXX: propagate exceptions from loader threads     link switcher +    -- transfer the references+    let rsFontAssets = uiSettings.fontBundles+     pure RunState{..}  initialFrameResources-  :: (Traversable fonts, Traversable textures)-  => UI.Settings fonts textures+  :: UI.Settings   -> Queues Vk.CommandPool   -> Basic.RenderPasses   -> Basic.Pipelines
src/Stage/Loader/Types.hs view
@@ -8,6 +8,7 @@  import Render.Basic qualified as Basic import Render.DescSets.Set0 qualified as Set0+import Resource.Font.Ktxf qualified as Ktxf  import Stage.Loader.UI (UI) import Stage.Loader.UI qualified as UI@@ -26,4 +27,6 @@   { rsSceneUiP    :: Set0.Process    , rsUI :: UI++  , rsFontAssets :: [Ktxf.Bundle] -- have to hold the bundle reference while it is used   }
src/Stage/Loader/UI.hs view
@@ -30,22 +30,22 @@ import Render.Unlit.Textured.Model qualified as UnlitTextured import Resource.Buffer qualified as Buffer import Resource.Combined.Textures qualified as CombinedTextures-import Resource.Font.EvanW qualified as EvanW+import Resource.Font.Ktxf qualified as Ktxf import Resource.Model qualified as Model import Resource.Model.Observer qualified as Observer import Resource.Region qualified as Region import Resource.Texture (Texture, Flat) -data Settings fonts textures = Settings+data Settings = Settings   { titleMessage :: Text   , backgroundIx :: Int32   , spinnerIx    :: Int32 -  , combined     :: CombinedTextures.Collection textures fonts (Int32, Texture Flat)-  , fonts        :: fonts EvanW.Container+  , combined     :: CombinedTextures.Collection [] [] (Int32, Texture Flat) -  , smallFont    :: forall a . fonts a -> a-  , largeFont    :: forall a . fonts a -> a+  , smallFont    :: Ktxf.Stack+  , largeFont    :: Ktxf.Stack+  , fontBundles :: [Ktxf.Bundle]   }  data UI = UI@@ -63,7 +63,7 @@  spawn   :: Queues Vk.CommandPool-  -> Settings fonts textures+  -> Settings   -> Engine.StageRIO env (Resource.ReleaseKey, UI) spawn pools Settings{..} = Region.run do   screenBoxP <- Camera.trackOrthoPixelsCentered@@ -150,11 +150,12 @@   where     title = Message.Input       { inputText         = titleMessage+      , inputLineSize     = 1.5+      , inputBreak        = Ktxf.Greedy       , inputOrigin       = Alignment.centerBottom       , inputSize         = 64       , inputColor        = vec4 1 0.5 0.25 1-      , inputFont         = largeFont fonts-      , inputFontId       = fst . largeFont $ CombinedTextures.fonts combined+      , inputFont         = largeFont       , inputOutline      = vec4 0 0 0 1       , inputOutlineWidth = 4/16       , inputSmoothing    = 1/16@@ -162,11 +163,12 @@      subtitle = Message.Input       { inputText         = "Loading..."+      , inputLineSize     = 1.5+      , inputBreak        = Ktxf.Greedy       , inputOrigin       = Alignment.centerTop       , inputSize         = 32       , inputColor        = vec4 0.5 1 0.25 1-      , inputFont         = smallFont fonts-      , inputFontId       = fst . smallFont $ CombinedTextures.fonts combined+      , inputFont         = smallFont       , inputOutline      = vec4 0 0 0 1       , inputOutlineWidth = 4/16       , inputSmoothing    = 1/16@@ -174,11 +176,12 @@      progress = Message.Input       { inputText         = ""+      , inputLineSize     = 1.5+      , inputBreak        = Ktxf.Greedy       , inputOrigin       = Alignment.centerBottom       , inputSize         = 16       , inputColor        = vec4 0.25 0.5 1 1-      , inputFont         = smallFont fonts-      , inputFontId       = fst . smallFont $ CombinedTextures.fonts combined+      , inputFont         = smallFont       , inputOutline      = 0       , inputOutlineWidth = 0       , inputSmoothing    = 1/16