fir-0.1.0.0: src/FIR/Validation/Arrayness.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-|
Module: FIR.Validation.Arrayness
Validation of implicit arrayness.
Non-patch inputs to tessellation and geometry shaders,
and non-patch outputs of tessellation control shaders,
have an added layer of arrayness.
This module checks for the presence of these layers of arrayness,
and provides type families to peel off such implicit arrayness layers.
-}
module FIR.Validation.Arrayness where
-- base
import Data.Kind
( Type )
import Data.Type.Bool
( If )
import GHC.TypeLits
( Symbol
, TypeError, ErrorMessage(..)
)
import GHC.TypeNats
( Nat )
-- fir
import Data.Type.List
( Elem )
import Data.Type.String
( ShowNat )
import FIR.Prim.Array
( Array )
import qualified SPIRV.Decoration as SPIRV
( Decoration(..) )
import qualified SPIRV.Stage as SPIRV
( ExecutionModel
, ExecutionInfo(..), ShaderInfo(..)
, NamedExecutionModel
)
import qualified SPIRV.Storage as SPIRV
( StorageClass(Input, Output) )
-------------------------------------------------------------
data IsPatchVariable
= IsPatch
| NotPatch
data ImplicitArrayness
= NoImplicitArrayness IsPatchVariable
| ImplicitArrayness Nat
type family UnderlyingType
( varName :: Symbol )
( inOut :: SPIRV.StorageClass )
( decs :: [ SPIRV.Decoration Nat ] )
( emName :: Symbol )
( ty :: Type )
( xnfo :: SPIRV.ExecutionInfo Nat em )
:: Type
where
UnderlyingType varName inOut decs emName ty ( xnfo :: SPIRV.ExecutionInfo Nat em )
= RemoveArrayness varName inOut emName em ty
( Arrayness inOut decs xnfo )
type family Arrayness
( inOut :: SPIRV.StorageClass )
( decs :: [ SPIRV.Decoration Nat ] )
( xnfo :: SPIRV.ExecutionInfo Nat em )
:: ImplicitArrayness
where
Arrayness SPIRV.Input decs
( SPIRV.ShaderExecutionInfo ( SPIRV.TessellationControlShaderInfo i _ _ ) )
= If ( SPIRV.Patch `Elem` decs )
( 'NoImplicitArrayness IsPatch )
( 'ImplicitArrayness i )
Arrayness SPIRV.Input decs
( SPIRV.ShaderExecutionInfo ( SPIRV.TessellationEvaluationShaderInfo i _ ) )
= If ( SPIRV.Patch `Elem` decs )
( 'NoImplicitArrayness IsPatch )
( 'ImplicitArrayness i )
Arrayness SPIRV.Input decs
( SPIRV.ShaderExecutionInfo ( SPIRV.GeometryShaderInfo i _ ) )
= 'ImplicitArrayness i
Arrayness SPIRV.Output decs
( SPIRV.ShaderExecutionInfo ( SPIRV.TessellationControlShaderInfo _ j _ ) )
= If ( SPIRV.Patch `Elem` decs )
( 'NoImplicitArrayness IsPatch )
( 'ImplicitArrayness j )
Arrayness _ _ _ = 'NoImplicitArrayness NotPatch
type family RemoveArrayness
( varName :: Symbol )
( inOut :: SPIRV.StorageClass )
( emName :: Symbol )
( em :: SPIRV.ExecutionModel )
( ty :: Type )
( arrayness :: ImplicitArrayness )
:: Type
where
RemoveArrayness _ _ _ _ ty (NoImplicitArrayness _) = ty
-- if an implicit level of arrayness is detected,
-- remove it (ignoring the specific array length, as Vulkan does)
RemoveArrayness _ _ _ _ (Array _ a) ('ImplicitArrayness _) = a
RemoveArrayness varName inOut emName em ty ('ImplicitArrayness i)
= TypeError
( ShowType inOut :<>: Text " per-vertex variable " :<>: ShowType varName
:<>: Text " of " :<>: Text (SPIRV.NamedExecutionModel emName em)
:<>: Text " should be an array,"
:$$: Text "but its type is " :<>: ShowType ty :<>: Text "."
)
type family ArraynessNote
( em1 :: SPIRV.ExecutionModel )
( name1 :: Symbol )
( arrayness1 :: ImplicitArrayness )
( em2 :: SPIRV.ExecutionModel )
( name2 :: Symbol )
( arrayness2 :: ImplicitArrayness )
:: ErrorMessage
where
ArraynessNote
em1 name1 ('ImplicitArrayness i1)
_ _ ('NoImplicitArrayness _)
= Text ""
:$$: Text "Note that per-vertex outputs of "
:<>: Text ( SPIRV.NamedExecutionModel name1 em1 )
:<>: Text " must be arrays of size " :<>: Text (ShowNat i1) :<>: Text "."
ArraynessNote
_ _ ('NoImplicitArrayness _)
em2 name2 ('ImplicitArrayness i2)
= Text ""
:$$: Text "Note that per-vertex inputs of "
:<>: Text ( SPIRV.NamedExecutionModel name2 em2 )
:<>: Text " must be arrays of size " :<>: Text (ShowNat i2) :<>: Text "."
ArraynessNote
em1 name1 ('ImplicitArrayness i1)
em2 name2 ('ImplicitArrayness i2)
= Text ""
:$$: Text "Note that:"
:$$: Text " - per-vertex outputs of "
:<>: Text ( SPIRV.NamedExecutionModel name1 em1 )
:<>: Text " must be arrays of size " :<>: Text (ShowNat i1) :<>: Text ","
:$$: Text " - per-vertex inputs of "
:<>: Text ( SPIRV.NamedExecutionModel name2 em2 )
:<>: Text " must be arrays of size " :<>: Text (ShowNat i2) :<>: Text "."
ArraynessNote
_ _ ('NoImplicitArrayness _)
_ _ ('NoImplicitArrayness _)
= Text ""