packages feed

mischief-render-0.1.0.0: src/Mischief/Shader/Internal/State.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeAbstractions #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

module Mischief.Shader.Internal.State where

import Control.Monad.State
import Data.Data hiding (cast)
import Data.Map (Map)
import Data.Singletons (SingI, demote)
import Data.Text (Text)
import Data.Text qualified as T
import GHC.TypeLits (Nat, type (+))
import Mischief.Shader.Internal.Singletons
import Unsafe.Coerce (unsafeCoerce)
import Prelude hiding (cos, sin)

data ErasedValue where
  ErasedValue :: a -> ErasedValue

data ShaderState = ShaderState
  { counter :: VarIndex,
    ast :: Stmt,
    dict :: Map TypeRep ErasedValue
  }

newtype VarIndex = VarIndex Integer
  deriving newtype (Show, Num)

nameIndex :: VarIndex -> Text
nameIndex i = "v" <> T.pack (show i)

data Stmt where
  Concat :: Stmt -> Stmt -> Stmt
  Let :: VarIndex -> Expr a -> Stmt
  Return :: Expr a -> Stmt
  Debug :: Text -> Stmt
  Empty :: Stmt

data CustomType a = CustomType

instance Eq (CustomType a) where
  _ == _ = False

data Expr (a :: Types) where
  Function :: Text -> [Param] -> Expr a
  Add :: (SingI a) => Expr a -> Expr a -> Expr a
  Sub :: (SingI a) => Expr a -> Expr a -> Expr a
  Mult :: (SingI a) => Expr a -> Expr a -> Expr a
  Div :: (SingI a) => Expr a -> Expr a -> Expr a
  Neg :: (SingI a) => Expr a -> Expr a
  Abs :: (SingI a) => Expr a -> Expr a
  Signum :: (SingI a) => Expr a -> Expr a
  Cast :: (SingI a, SingI b) => Proxy b -> Expr a -> Expr b
  CastVec :: (SingI (VectorType n a), SingI (VectorType n b)) => Proxy (VectorType n b) -> Expr (VectorType n a) -> Expr (VectorType n b)
  ConstantInt :: Int -> Expr (Primitive TInt)
  ConstantFloat :: Float -> Expr (Primitive TFloat)
  ConstantUInt :: Int -> Expr (Primitive TUInt)
  ConstantArray :: forall (a' :: Types). (SingI a') => (Proxy a') -> [Expr a'] -> Expr (ArrayType a')
  ConstantVec :: forall (n :: Nat) (a' :: PrimitiveTypes). (PrintVecElements n a', SingI (VectorType n a')) => Proxy (VectorType n a') -> VecInit n a' -> Expr (VectorType n a')
  ConstantMat :: forall (n :: Nat) (m :: Nat). (PrintMatElements n m, SingI (MatrixType n m)) => Proxy (MatrixType n m) -> MatInit n m -> Expr (MatrixType n m)
  AccessField :: (SingI a) => Expr a -> Text -> Expr b
  Index :: (SingI (ArrayType a)) => Expr (ArrayType a) -> Expr (Primitive TUInt) -> Expr a
  Var :: (SingI a) => VarIndex -> Expr a
  BindingVar :: (SingI a) => Integer -> Expr a
  BuiltInVar :: (SingI a) => Text -> Expr a
  StructInit :: Text -> [Text] -> Expr a
  LocationVar :: (SingI a) => Integer -> Expr a
  VarCustom :: Text -> Expr a
  CustomOperator :: Text -> Expr a -> Expr b -> Expr c
  Void :: (SingI a) => Expr a
  AppendVecs :: forall (n :: Nat) (m :: Nat) a. (SingI a, PrintVecElements (n + m) a, SingI (VectorType (n + m) a)) => Proxy (VectorType (n + m) a) -> Expr (VectorType n a) -> Expr (VectorType m a) -> Expr (VectorType (n + m) a)
  ExtendVec :: forall (n :: Nat) a. (SingI a, PrintVecElements (n + 1) a, SingI (VectorType (n + 1) a)) => Proxy (VectorType (n + 1) a) -> Expr (VectorType n a) -> Expr (Primitive a) -> Expr (VectorType (n + 1) a)
  ConstantBool :: Bool -> Expr TBool
  If :: Expr TBool -> Expr a -> Expr a -> Expr a

data Param where
  Param :: (SingI a) => Expr a -> Param

type family VectorOrSingleOf a where
  VectorOrSingleOf (VectorType n a) = a
  VectorOrSingleOf (Primitive a) = a

instance (SingI a) => Num (Expr (Primitive a)) where
  (+) = Add
  (*) = Mult
  abs = Abs
  signum = Signum
  fromInteger i =
    let t = demote @a
     in case t of
          TInt -> unsafeCoerce $ ConstantInt (fromInteger i)
          TFloat -> unsafeCoerce $ ConstantFloat (fromInteger i)
          TUInt -> unsafeCoerce $ ConstantUInt (fromInteger i)
  negate = Neg

instance Fractional (Expr (Primitive TFloat)) where
  fromRational = ConstantFloat . fromRational
  recip = Div 1
  (/) = Div

type family VecInit (n :: Nat) (a :: PrimitiveTypes) where
  VecInit 2 a = (Expr (Primitive a), Expr (Primitive a))
  VecInit 3 a = (Expr (Primitive a), Expr (Primitive a), Expr (Primitive a))
  VecInit 4 a = (Expr (Primitive a), Expr (Primitive a), Expr (Primitive a), Expr (Primitive a))

class PrintVecElements (n :: Nat) (a :: PrimitiveTypes) where
  printVecElements :: VecInit n a -> Text

instance PrintVecElements 2 a where
  printVecElements (a, b) = exprToWGSL a <> ", " <> exprToWGSL b

instance PrintVecElements 3 a where
  printVecElements (a, b, c) = exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c

instance PrintVecElements 4 a where
  printVecElements (a, b, c, d) = exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c <> ", " <> exprToWGSL d

type family ArrayInit (n :: Nat) a where
  ArrayInit 2 a = (Expr a, Expr a)
  ArrayInit 3 a = (Expr a, Expr a, Expr a)
  ArrayInit 4 a = (Expr a, Expr a, Expr a, Expr a)
  ArrayInit 5 a = (Expr a, Expr a, Expr a, Expr a, Expr a)
  ArrayInit 6 a = (Expr a, Expr a, Expr a, Expr a, Expr a, Expr a)

class PrintArrayElements (n :: Nat) (a :: Types) where
  printArrayElements :: ArrayInit n a -> Text

instance PrintArrayElements 2 a where
  printArrayElements (a, b) = exprToWGSL a <> ", " <> exprToWGSL b

instance PrintArrayElements 3 a where
  printArrayElements (a, b, c) = exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c

instance PrintArrayElements 4 a where
  printArrayElements (a, b, c, d) = exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c <> ", " <> exprToWGSL d

instance PrintArrayElements 5 a where
  printArrayElements (a, b, c, d, e) = exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c <> ", " <> exprToWGSL d <> ", " <> exprToWGSL e

instance PrintArrayElements 6 a where
  printArrayElements (a, b, c, d, e, f) = exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c <> ", " <> exprToWGSL d <> ", " <> exprToWGSL e <> ", " <> exprToWGSL f

type family MatInit (n :: Nat) (m :: Nat) where
  MatInit 2 a = (Expr (VectorType a TFloat), Expr (VectorType a TFloat))
  MatInit 3 a = (Expr (VectorType a TFloat), Expr (VectorType a TFloat), Expr (VectorType a TFloat))
  MatInit 4 a = (Expr (VectorType a TFloat), Expr (VectorType a TFloat), Expr (VectorType a TFloat), Expr (VectorType a TFloat))

class PrintMatElements (n :: Nat) (m :: Nat) where
  printMatElements :: MatInit n m -> Text

instance PrintMatElements 2 n where
  printMatElements (a, b) =
    exprToWGSL a <> ", " <> exprToWGSL b

instance PrintMatElements 3 n where
  printMatElements (a, b, c) =
    exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c

instance PrintMatElements 4 n where
  printMatElements (a, b, c, d) =
    exprToWGSL a <> ", " <> exprToWGSL b <> ", " <> exprToWGSL c <> ", " <> exprToWGSL d

showArgs :: [Param] -> Text
showArgs [] = ""
showArgs [Param x] = exprToWGSL x
showArgs ((Param x) : xs) = exprToWGSL x <> ", " <> showArgs xs

exprToWGSL :: Expr a -> Text
exprToWGSL (Function t x) = t <> "(" <> showArgs x <> ")"
exprToWGSL (Add a b) = "(" <> exprToWGSL a <> " + " <> exprToWGSL b <> ")"
exprToWGSL (Mult a b) = "(" <> exprToWGSL a <> " * " <> exprToWGSL b <> ")"
exprToWGSL (Sub a b) = "(" <> exprToWGSL a <> " - " <> exprToWGSL b <> ")"
exprToWGSL (Div a b) = "(" <> exprToWGSL a <> " / " <> exprToWGSL b <> ")"
exprToWGSL (Neg a) = "(-" <> exprToWGSL a <> ")"
exprToWGSL (Abs a) = exprToWGSL (Function "abs" [Param a])
exprToWGSL (Signum a) = exprToWGSL (Function "sign" [Param a])
exprToWGSL (Cast (_ :: Proxy b) (a :: Expr a)) =
  let aType = demote @a
      bType = demote @b
   in if aType == bType
        then exprToWGSL a
        else case bType of
          Primitive TInt -> "i32(" <> exprToWGSL a <> ")"
          Primitive TFloat -> "f32(" <> exprToWGSL a <> ")"
          Primitive TUInt -> "u32(" <> exprToWGSL a <> ")"
          _ -> undefined
exprToWGSL (CastVec (_ :: Proxy b) (a :: Expr a)) =
  let aType = demote @a
      bType = demote @b
   in if aType == bType
        then exprToWGSL a
        else case bType of
          VectorType 2 TInt -> "vec2<i32>(" <> exprToWGSL a <> ")"
          VectorType 2 TFloat -> "vec2<f32>(" <> exprToWGSL a <> ")"
          VectorType 2 TUInt -> "vec2<u32>(" <> exprToWGSL a <> ")"
          VectorType 3 TInt -> "vec3<i32>(" <> exprToWGSL a <> ")"
          VectorType 3 TFloat -> "vec3<f32>(" <> exprToWGSL a <> ")"
          VectorType 3 TUInt -> "vec3<u32>(" <> exprToWGSL a <> ")"
          VectorType 4 TInt -> "vec4<i32>(" <> exprToWGSL a <> ")"
          VectorType 4 TFloat -> "vec4<f32>(" <> exprToWGSL a <> ")"
          VectorType 4 TUInt -> "vec4<u32>(" <> exprToWGSL a <> ")"
          _ -> undefined
exprToWGSL (ConstantArray (_ :: Proxy a) x) =
  let element = typeToWGSL' (demote @a)
   in "array<" <> element <> ", " <> T.show (length x) <> ">(" <> T.intercalate ", " (map exprToWGSL x) <> ")"
exprToWGSL (ConstantVec @n @a' (_ :: Proxy a) x) =
  let elements = printVecElements @n @a' x
   in case demote @a of
        VectorType 2 TInt -> "vec2<i32>(" <> elements <> ")"
        VectorType 2 TFloat -> "vec2<f32>(" <> elements <> ")"
        VectorType 2 TUInt -> "vec2<u32>(" <> elements <> ")"
        VectorType 3 TInt -> "vec3<i32>(" <> elements <> ")"
        VectorType 3 TFloat -> "vec3<f32>(" <> elements <> ")"
        VectorType 3 TUInt -> "vec3<u32>(" <> elements <> ")"
        VectorType 4 TInt -> "vec4<i32>(" <> elements <> ")"
        VectorType 4 TFloat -> "vec4<f32>(" <> elements <> ")"
        VectorType 4 TUInt -> "vec4<u32>(" <> elements <> ")"
        _ -> undefined
exprToWGSL (ConstantMat @n @m (_ :: Proxy a) x) =
  let elements = printMatElements @n @m x
   in case demote @a of
        MatrixType 2 2 -> "mat2x2<f32>(" <> elements <> ")"
        MatrixType 3 2 -> "mat2x3<f32>(" <> elements <> ")"
        MatrixType 4 2 -> "mat2x4<f32>(" <> elements <> ")"
        MatrixType 2 3 -> "mat3x2<f32>(" <> elements <> ")"
        MatrixType 3 3 -> "mat3x3<f32>(" <> elements <> ")"
        MatrixType 4 3 -> "mat3x4<f32>(" <> elements <> ")"
        MatrixType 2 4 -> "mat4x2<f32>(" <> elements <> ")"
        MatrixType 3 4 -> "mat4x3<f32>(" <> elements <> ")"
        MatrixType 4 4 -> "mat4x4<f32>(" <> elements <> ")"
        _ -> undefined
exprToWGSL (ConstantInt x) = T.pack $ show x <> "i"
exprToWGSL (ConstantFloat x) = T.pack $ show x <> "f"
exprToWGSL (ConstantUInt x) = T.pack $ show x <> "u"
exprToWGSL (Var x) = nameIndex x
exprToWGSL (BindingVar x) = "b" <> T.pack (show x)
exprToWGSL Void = ""
exprToWGSL (AccessField x f) = exprToWGSL x <> "." <> f
exprToWGSL (Index a i) = exprToWGSL a <> "[" <> exprToWGSL i <> "]"
exprToWGSL (BuiltInVar x) = "input." <> x
exprToWGSL (LocationVar x) = "input.l" <> T.pack (show x)
exprToWGSL (StructInit "" [p]) = p
exprToWGSL (StructInit name params) = name <> "(" <> T.intercalate "," params <> ")"
exprToWGSL (CustomOperator op a b) = "(" <> exprToWGSL a <> " " <> op <> " " <> exprToWGSL b <> ")"
exprToWGSL (VarCustom t) = t
exprToWGSL (AppendVecs (_ :: Proxy v) a b) = case demote @v of
  VectorType 2 TInt -> "vec2<i32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 2 TFloat -> "vec2<f32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 2 TUInt -> "vec2<u32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 3 TInt -> "vec3<i32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 3 TFloat -> "vec3<f32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 3 TUInt -> "vec3<u32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 4 TInt -> "vec4<i32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 4 TFloat -> "vec4<f32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 4 TUInt -> "vec4<u32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  _ -> undefined
exprToWGSL (ExtendVec (_ :: Proxy v) a b) = case demote @v of
  VectorType 2 TInt -> "vec2<i32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 2 TFloat -> "vec2<f32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 2 TUInt -> "vec2<u32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 3 TInt -> "vec3<i32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 3 TFloat -> "vec3<f32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 3 TUInt -> "vec3<u32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 4 TInt -> "vec4<i32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 4 TFloat -> "vec4<f32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  VectorType 4 TUInt -> "vec4<u32>(" <> exprToWGSL a <> "," <> exprToWGSL b <> ")"
  _ -> undefined
exprToWGSL (ConstantBool b) = if b then "true" else "false"
exprToWGSL (If bool a b) = "if " <> exprToWGSL bool <> " {" <> exprToWGSL a <> "} else {" <> exprToWGSL b <> "}"

stmtToWGSL :: Stmt -> Text
stmtToWGSL Empty = ""
stmtToWGSL (Concat Empty a) = stmtToWGSL a
stmtToWGSL (Concat a b) = stmtToWGSL a <> "\n" <> stmtToWGSL b
stmtToWGSL (Let a b) = "  let " <> nameIndex a <> " = " <> exprToWGSL b <> ";"
stmtToWGSL (Return a) = "  return " <> exprToWGSL a <> ";"
stmtToWGSL (Debug t) = "  //" <> t

-- stmtToWGSL (VLet a b) = "  var " <> nameIndex a <> " = " <> exprToWGSL b <> ";"

newtype Shader m a = Shader (State ShaderState a) deriving newtype (Functor, Applicative, Monad, MonadState ShaderState)

typeToWGSL :: forall (a :: Types). (SingI a) => Expr a -> Text
typeToWGSL _ = typeToWGSL' (demote @a)

typeToWGSL' :: Types -> Text
typeToWGSL' a = case a of
  Primitive TInt -> "i32"
  Primitive TFloat -> "f32"
  Primitive TUInt -> "u32"
  -- ArrayType n t -> "array<" <> typeToWGSL' t <> ", " <> T.pack (show n) <> ">"
  VectorType 2 TFloat -> "vec2<f32>"
  VectorType 2 TInt -> "vec2<i32>"
  VectorType 2 TUInt -> "vec2<u32>"
  VectorType 3 TFloat -> "vec3<f32>"
  VectorType 3 TInt -> "vec3<i32>"
  VectorType 3 TUInt -> "vec3<u32>"
  VectorType 4 TFloat -> "vec4<f32>"
  VectorType 4 TInt -> "vec4<i32>"
  VectorType 4 TUInt -> "vec4<u32>"
  MatrixType 2 2 -> "mat2x2<f32>"
  MatrixType 2 3 -> "mat2x3<f32>"
  MatrixType 2 4 -> "mat2x4<f32>"
  MatrixType 3 2 -> "mat3x2<f32>"
  MatrixType 3 3 -> "mat3x3<f32>"
  MatrixType 3 4 -> "mat3x4<f32>"
  MatrixType 4 2 -> "mat4x2<f32>"
  MatrixType 4 3 -> "mat4x3<f32>"
  MatrixType 4 4 -> "mat4x4<f32>"
  _ -> undefined

addStmt :: Stmt -> Shader m ()
addStmt stmt = do
  state <- get
  put state {ast = Concat state.ast stmt}

newVarIndex :: Shader m VarIndex
newVarIndex = do
  ShaderState {counter, ast, dict} <- get
  put (ShaderState {counter = counter + 1, ast, dict})
  pure counter

var :: (SingI a) => Expr a -> Shader m (Expr a)
var expr = do
  index <- newVarIndex
  addStmt (Let index expr)
  pure $ Var index

comment :: Text -> Shader m ()
comment t = addStmt (Debug t)

data CPU

data GPU

data Internal