glsl-0.0.1.0: src/Language/GLSL/Runtime/Eval.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE ViewPatterns #-}
module Language.GLSL.Runtime.Eval where
import Control.Lens ((^.))
import Control.Monad (foldM, foldM_)
import Control.Monad.Trans.State.Strict (evalStateT, get, modify')
import qualified Data.IntMap as M
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.IO as IO
import qualified Debug.Trace as Trace
import Language.GLSL.AST
import Language.GLSL.Decls (addDecl, addDeclN, addDeclNE,
emptyDecls, getDeclNE,
toUniformId)
import Language.GLSL.Parser (parseShader)
import Language.GLSL.PrettyPrint (pp, ppExpr, ppGlobalDecl,
ppNameExpr, ppSwizzle,
ppVecIndex)
import qualified Language.GLSL.Runtime.PrimFuns as PrimFuns
import Language.GLSL.Runtime.Value (Eval, EvalResult (..),
EvalState (..), Proc (..),
Value (..), defaultValue,
evalBinaryOp, evalCoerce,
evalUnaryOp, isNaNValue,
roundValue)
import Linear (R1 (..), R2 (..), R3 (..),
R4 (..))
traceAssignments :: Bool
traceAssignments = False
trace :: String -> a -> a
trace = if traceAssignments then Trace.trace else const id
eval :: LT.Text -> Either String Value
eval code = do
glsl <- parseShader code
fromResult $ evalStateT (evalGLSL glsl) startState
startState :: EvalState
startState = EvalState
{ stProcs = M.empty
, stMainProc = Nothing
, globals = emptyDecls
, gl_Position = Nothing
}
evalGLSL :: GLSL () -> Eval Value
evalGLSL (GLSL _ d) = do
mapM_ discoverTopDecl d
evalMain
-- maybe (FloatValue 0) id . gl_Position <$> get
roundValue <$> getValue emptyLocals (NameExpr (Name NsOut (NameId 0)))
discoverTopDecl :: TopDecl () -> Eval ()
discoverTopDecl (LayoutDecl _ d) = discoverGlobalDecl d
discoverTopDecl (GlobalDecl d) = discoverGlobalDecl d
discoverTopDecl (ProcDecl ProcMain params body) =
modify' $ \st -> st{stMainProc = Just $ Proc params body}
discoverTopDecl (ProcDecl (ProcName (NameId n)) params body) =
modify' $ \st@EvalState{..} -> st{stProcs = M.insert n (Proc params body) stProcs}
discoverGlobalDecl :: GlobalDecl -> Eval ()
discoverGlobalDecl (GDecl GkUniform (TyStruct _ fields) (Name NsU n)) =
modify' $ \st@EvalState{..} ->
st{globals = foldr (\(ty, m) -> addDecl NsU (toUniformId (n, m)) (defaultValue ty)) globals fields}
discoverGlobalDecl (GDecl GkOut ty n) =
modify' $ \st@EvalState{..} -> st{globals = addDeclN n (defaultValue ty) globals}
discoverGlobalDecl (GDecl GkIn ty n) =
modify' $ \st@EvalState{..} -> st{globals = addDeclN n (defaultValue ty) globals}
discoverGlobalDecl d@(GDecl GkUniform _ _) =
fail $ "unsupported uniform type in decl: " <> pp ppGlobalDecl d
evalMain :: Eval ()
evalMain = do
Just mainProc <- stMainProc <$> get
evalProc mainProc []
return ()
newtype LocalState = LocalState
{ temps :: M.IntMap Value
}
emptyLocals :: LocalState
emptyLocals = LocalState
{ temps = M.empty
}
evalProc :: Proc -> [Value] -> Eval ()
evalProc (Proc _params ss) _args =
foldM_ evalStmtAnnot emptyLocals ss
evalStmtAnnot :: LocalState -> StmtAnnot () -> Eval LocalState
evalStmtAnnot lst (SA () s) = evalStmt lst s
evalStmt :: LocalState -> Stmt () -> Eval LocalState
evalStmt lst (AssignStmt n e) = do
v <- evalExpr lst e
setValue lst (NameExpr n) v
evalStmt lst (DeclStmt d) = evalLocalDecl lst d
evalStmt lst (EmitStmt e) = evalEmit lst e
evalStmt lst (IfStmt cond thens elses) = do
BoolValue v <- getValue lst (NameExpr (Name NsT cond))
if v
then foldM evalStmtAnnot lst thens
else foldM evalStmtAnnot lst elses
evalEmit :: LocalState -> Emit -> Eval LocalState
evalEmit lst EmitFragDepth = return lst
evalEmit lst (EmitPosition e) = do
v <- evalExpr lst e
modify' $ \st -> st{gl_Position = Just v}
return lst
evalLocalDecl :: LocalState -> LocalDecl -> Eval LocalState
evalLocalDecl lst (LDecl ty (NameExpr . Name NsT -> n) Nothing) =
let v = defaultValue ty in
setValue lst n v
evalLocalDecl lst (LDecl ty (NameExpr . Name NsT -> n) (Just e)) = do
v <- evalExpr lst e >>= evalCoerce ty
setValue lst n v
evalExpr :: LocalState -> Expr -> Eval Value
evalExpr lst = \case
BinaryExpr l op r -> do
lv <- evalExprAtom lst l
rv <- evalExprAtom lst r
return $ evalBinaryOp lv op rv
UnaryExpr op e -> do
v <- evalExprAtom lst e
return $ evalUnaryOp op v
AtomExpr e -> evalExprAtom lst e
FunCallExpr fun args -> do
vals <- mapM (evalExprAtom lst) args
PrimFuns.eval fun vals
e@TextureExpr{} ->
fail $ "texture() not implemented: " <> pp ppExpr e
evalExprAtom :: LocalState -> ExprAtom -> Eval Value
evalExprAtom lst = \case
LitFloatExpr _ f -> return $ FloatValue f
LitIntExpr _ i -> return $ IntValue i
IdentifierExpr n -> getValue lst n
SwizzleExpr n s -> getValue lst (NameExpr (Name NsT n)) >>= evalVecIndex s
VecIndexExpr n i -> getValue lst n >>= evalVecIndex i
MatIndexExpr n i j -> getValue lst n >>= evalMatIndex i j
evalVecIndex :: Swizzle -> Value -> Eval Value
evalVecIndex X (Vec2Value v) = return $ FloatValue $ v ^. _x
evalVecIndex Y (Vec2Value v) = return $ FloatValue $ v ^. _y
evalVecIndex X (Vec3Value v) = return $ FloatValue $ v ^. _x
evalVecIndex Y (Vec3Value v) = return $ FloatValue $ v ^. _y
evalVecIndex Z (Vec3Value v) = return $ FloatValue $ v ^. _z
evalVecIndex X (Vec4Value v) = return $ FloatValue $ v ^. _x
evalVecIndex Y (Vec4Value v) = return $ FloatValue $ v ^. _y
evalVecIndex Z (Vec4Value v) = return $ FloatValue $ v ^. _z
evalVecIndex W (Vec4Value v) = return $ FloatValue $ v ^. _w
evalVecIndex s v = fail $ "cannot access " <> pp ppSwizzle s <> " on " <> show v
evalMatIndex :: Swizzle -> Swizzle -> Value -> Eval Value
evalMatIndex i j (Mat4x4Value v) =
return $ FloatValue $ v ^. (swizzle i . swizzle j)
where
swizzle X = _x
swizzle Y = _y
swizzle Z = _z
swizzle W = _w
evalMatIndex i j v =
fail $ "cannot access [" <> pp ppVecIndex i <> "][" <> pp ppVecIndex j <> "]"
<> " on " <> show v
setValue :: LocalState -> NameExpr -> Value -> Eval LocalState
setValue lst@LocalState{..} n@(NameExpr (Name NsT (NameId nId))) v =
trace (pp ppNameExpr n <> " = " <> show v) $
if isNaNValue v
then fail $ pp ppNameExpr n <> " = " <> show v
else return lst{temps = M.insert nId v temps}
setValue lst n v = do
modify' $ \st@EvalState{..} -> st{globals = addDeclNE n v globals}
trace (pp ppNameExpr n <> " = " <> show v) $
if isNaNValue v
then fail $ pp ppNameExpr n <> " = " <> show v
else return lst
getValue :: LocalState -> NameExpr -> Eval Value
getValue LocalState{..} (NameExpr (Name NsT (NameId n))) = do
let Just v = M.lookup n temps
return v
getValue _ n = do
v <- getDeclNE n . globals <$> get
case v of
Nothing -> fail $ "undefined global: " <> pp ppNameExpr n
Just ok -> return ok
main :: IO ()
main = do
txt <- IO.readFile "../large-shaders/xax.vert"
print $ eval txt