module Render.ShadowMap.Pipeline
  ( Settings(..)
  , defaults

  , Pipeline
  , allocate

  , Config
  , config
  ) where

import RIO

import Control.Monad.Trans.Resource (ResourceT)
import Data.Tagged (Tagged(..))
import Geomancy (Transform)
import Vulkan.Core10 qualified as Vk

import Engine.Vulkan.Pipeline qualified as Pipeline
import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)
import Render.Code (compileVert, glsl)
import Render.Code.Lit (structLight)
import Render.DescSets.Set0 (vertexPos, instanceTransform)
import Render.DescSets.Sun (Sun, pattern MAX_VIEWS)

type Config = Pipeline.Configure Pipeline ()
type Pipeline = Pipeline.Pipeline '[Sun] () Transform

data Settings = Settings
  { Settings -> CullModeFlagBits
cull      :: Vk.CullModeFlagBits
  , Settings -> Maybe (Float, Float)
depthBias :: Maybe (Float, Float)
  }

defaults :: Settings
defaults :: Settings
defaults = Settings :: CullModeFlagBits -> Maybe (Float, Float) -> Settings
Settings
  { $sel:cull:Settings :: CullModeFlagBits
cull      = CullModeFlagBits
Vk.CULL_MODE_BACK_BIT
  , $sel:depthBias:Settings :: Maybe (Float, Float)
depthBias = (Float, Float) -> Maybe (Float, Float)
forall a. a -> Maybe a
Just (Float
2.0, Float
2.5)
  }

allocate
  :: ( HasVulkan env
     , HasRenderPass renderpass
     )
  => Tagged Sun DsBindings
  -> renderpass
  -> Settings
  -> ResourceT (RIO env) Pipeline
allocate :: Tagged Sun DsBindings
-> renderpass -> Settings -> ResourceT (RIO env) Pipeline
allocate Tagged Sun DsBindings
tset0 renderpass
rp Settings
settings = do
  (ReleaseKey
_, Pipeline
p) <- Maybe Extent2D
-> SampleCountFlagBits
-> Config '[Sun] () Transform ()
-> renderpass
-> ResourceT (RIO env) (ReleaseKey, Pipeline)
forall env (m :: * -> *) renderpass spec (dsl :: [*]) vertices
       instances.
(MonadVulkan env m, MonadResource m, HasRenderPass renderpass,
 Specialization spec, HasCallStack) =>
Maybe Extent2D
-> SampleCountFlagBits
-> Config dsl vertices instances spec
-> renderpass
-> m (ReleaseKey, Pipeline dsl vertices instances)
Pipeline.allocate
    Maybe Extent2D
forall a. Maybe a
Nothing
    SampleCountFlagBits
Vk.SAMPLE_COUNT_1_BIT
    (Tagged Sun DsBindings -> Settings -> Config
config Tagged Sun DsBindings
tset0 Settings
settings)
    renderpass
rp
  Pipeline -> ResourceT (RIO env) Pipeline
forall (f :: * -> *) a. Applicative f => a -> f a
pure Pipeline
p

config :: Tagged Sun DsBindings -> Settings -> Config
config :: Tagged Sun DsBindings -> Settings -> Config
config (Tagged DsBindings
set0) Settings{Maybe (Float, Float)
CullModeFlagBits
depthBias :: Maybe (Float, Float)
cull :: CullModeFlagBits
$sel:depthBias:Settings :: Settings -> Maybe (Float, Float)
$sel:cull:Settings :: Settings -> CullModeFlagBits
..} = Config '[] Any Any ()
forall vertices instances. Config '[] vertices instances ()
Pipeline.baseConfig
  { $sel:cDescLayouts:Config :: Tagged '[Sun] [DsBindings]
Pipeline.cDescLayouts  = [DsBindings] -> Tagged '[Sun] [DsBindings]
forall k (s :: k) b. b -> Tagged s b
Tagged @'[Sun] [DsBindings
set0]
  , $sel:cVertexCode:Config :: Maybe ByteString
Pipeline.cVertexCode   = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
vertCode
  , $sel:cVertexInput:Config :: SomeStruct PipelineVertexInputStateCreateInfo
Pipeline.cVertexInput  = SomeStruct PipelineVertexInputStateCreateInfo
vertexInput
  , $sel:cDepthBias:Config :: Maybe (Float, Float)
Pipeline.cDepthBias    = Maybe (Float, Float)
depthBias
  , $sel:cCull:Config :: CullModeFlagBits
Pipeline.cCull         = CullModeFlagBits
cull
  }
  where
    vertexInput :: SomeStruct PipelineVertexInputStateCreateInfo
vertexInput = [(VertexInputRate, [Format])]
-> SomeStruct PipelineVertexInputStateCreateInfo
Pipeline.vertexInput
      [ (VertexInputRate, [Format])
vertexPos
      , (VertexInputRate, [Format])
instanceTransform
      ]

vertCode :: ByteString
vertCode :: ByteString
vertCode =
  $(compileVert [glsl|
    #version 450
    #extension GL_ARB_separate_shader_objects : enable
    #extension GL_EXT_multiview : enable

    ${structLight}

    layout(set=0, binding=0, std140) uniform Globals {
      Light lights[${MAX_VIEWS}];
    };

    layout(location = 0) in vec3 vPosition;

    layout(location = 1) in mat4 iModel;

    void main() {
      gl_Position
        = lights[gl_ViewIndex].viewProjection
        * iModel
        * vec4(vPosition, 1.0);
    }
  |])