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

import RIO

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

import Engine.Vulkan.Pipeline qualified as Pipeline
import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)
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.Config '[Sun] () Transform
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 (dsl :: [*]) vertices
       instances.
(MonadVulkan env m, MonadResource m, HasRenderPass renderpass,
 HasCallStack) =>
Maybe Extent2D
-> SampleCountFlagBits
-> Config dsl vertices instances
-> 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 '[Sun] () Transform
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 '[Sun] () Transform
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 Any
forall a. Zero a => a
zero
  { $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);
    }
  |])