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 +33/−0
- embed/textures/black.ktx.zst binary
- embed/textures/flat.ktx.zst binary
- embed/textures/ibl_brdf_lut.ktx.zst binary
- embed/textures/white.ktx2 binary
- keid-render-basic.cabal +12/−10
- src/Engine/UI/Message.hs +24/−36
- src/Global/Resource/Texture/Base.hs +10/−5
- src/Render/Basic.hs +211/−191
- src/Render/Code/Lit.hs +26/−18
- src/Render/Font/EvanwSdf/Code.hs +0/−114
- src/Render/Font/EvanwSdf/Model.hs +0/−47
- src/Render/Font/EvanwSdf/Pipeline.hs +0/−64
- src/Render/Font/Msdf/Code.hs +167/−0
- src/Render/Font/Msdf/Model.hs +48/−0
- src/Render/Font/Msdf/Pipeline.hs +96/−0
- src/Render/Lit/Colored/Code.hs +2/−4
- src/Render/Lit/Colored/Pipeline.hs +20/−16
- src/Render/Lit/Material/Code.hs +3/−8
- src/Render/Lit/Material/Pipeline.hs +20/−16
- src/Render/Lit/Specialization.hs +51/−0
- src/Render/Lit/Textured/Code.hs +2/−5
- src/Render/Lit/Textured/Pipeline.hs +20/−16
- src/Render/ShadowMap/RenderPass.hs +4/−1
- src/Render/Unlit/Line2d/Model.hs +0/−1
- src/Resource/Font.hs +7/−18
- src/Resource/Font/EvanW.hs +0/−175
- src/Resource/Font/Ktxf.hs +141/−0
- src/Stage/Loader/Render.hs +1/−1
- src/Stage/Loader/Scene.hs +0/−1
- src/Stage/Loader/Setup.hs +38/−40
- src/Stage/Loader/Types.hs +3/−0
- src/Stage/Loader/UI.hs +16/−13
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