packages feed

glsl-0.0.0.1: src/Language/GLSL/Optimizer/DFG.hs

{-# LANGUAGE NamedFieldPuns  #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StrictData      #-}
module Language.GLSL.Optimizer.DFG where

import           Control.Monad.Trans.State (StateT, execState, get, modify',
                                            put)
import           Data.Foldable             (forM_)
import           Data.Functor              (void)
import           Data.Functor.Identity     (Identity)
import           Data.Graph.Inductive      (Node)
import qualified Data.Graph.Inductive      as G
import qualified Data.GraphViz             as GV
import qualified Data.GraphViz.Printing    as GV
import qualified Data.Text.Lazy.IO         as IO
import           Language.GLSL.Decls
import           Language.GLSL.Types       hiding (t)

--------------------------------------------------------------------------------

data DFG = DFG
  { gr         :: G.Gr DFGNode DFGEdge
  , decls      :: Decls Node
  , nextNodeId :: Node
  , ifCond     :: Maybe Node
  }

emptyDFG :: DFG
emptyDFG = DFG
  { gr = G.empty
  , decls = emptyDecls
  , nextNodeId = 0
  , ifCond = Nothing
  }

data DFGEdge = DFGEdge
  deriving (Show)

data DFGNode
  = DFGNode Namespace NameId
  deriving (Show)

type DFGState = StateT DFG Identity

genDFG :: GLSL a -> DFG
genDFG prog = execState (dfgGLSL prog) emptyDFG

dfgGLSL :: GLSL a -> DFGState ()
dfgGLSL (GLSL _ decls) = mapM_ dfgTopDecl decls

dfgTopDecl :: TopDecl a -> DFGState ()
dfgTopDecl (LayoutDecl _ d)     = dfgGlobalDecl d
dfgTopDecl (GlobalDecl d)       = dfgGlobalDecl d
dfgTopDecl (ProcDecl _ _ stmts) = mapM_ dfgStmtAnnot stmts

dfgGlobalDecl :: GlobalDecl -> DFGState ()
dfgGlobalDecl (GDecl _ (TyStruct _ ms) (Name NsU n)) =
  mapM_ (dfgStructMember n) ms
dfgGlobalDecl (GDecl _ _ (Name ns n)) =
  void $ addNode ns n

dfgStructMember :: NameId -> (Type, NameId) -> DFGState ()
dfgStructMember n (_, m) = void $ addNode NsU (toUniformId (n, m))

dfgStmt :: Stmt a -> DFGState ()
dfgStmt (DeclStmt d)     = dfgLocalDecl d
dfgStmt (AssignStmt n e) = do
  targetId <- nodeForName n
  DFG{ifCond} <- get
  forM_ ifCond (addEdge targetId)
  dfgExpr e targetId
dfgStmt (IfStmt c t e) = do
  ifCond <- Just <$> nodeFor NsT c
  modify' $ \dfg -> dfg{ifCond}
  mapM_ dfgStmtAnnot (t ++ e)
  modify' $ \dfg -> dfg{ifCond=Nothing}
dfgStmt _                = return ()

dfgStmtAnnot :: StmtAnnot a -> DFGState ()
dfgStmtAnnot (SA _ s) = dfgStmt s

dfgLocalDecl :: LocalDecl -> DFGState ()
dfgLocalDecl (LDecl _ n e) = do
  nodeId <- addNode NsT n
  maybe (return ()) (`dfgExpr` nodeId) e

dfgExpr :: Expr -> Node -> DFGState ()
dfgExpr (FunCallExpr _ args) declNode = mapM_ (`dfgExprAtom` declNode) args
dfgExpr (TextureExpr t x y) declNode  = mapM_ (`dfgExprAtom` declNode) [t, x, y]
dfgExpr (UnaryExpr _ e) declNode      = dfgExprAtom e declNode
dfgExpr (AtomExpr e) declNode         = dfgExprAtom e declNode
dfgExpr (BinaryExpr l _ r) declNode   = mapM_ (`dfgExprAtom` declNode) [l, r]

dfgExprAtom :: ExprAtom -> Node -> DFGState ()
dfgExprAtom LitIntExpr{} _                = return ()
dfgExprAtom LitFloatExpr{} _              = return ()
dfgExprAtom (IdentifierExpr n) declNode   = nodeForNameExpr n >>= addEdge declNode
dfgExprAtom (SwizzleExpr n _)  declNode   = nodeFor NsT n >>= addEdge declNode
dfgExprAtom (VecIndexExpr n _) declNode   = nodeForNameExpr n >>= addEdge declNode
dfgExprAtom (MatIndexExpr n _ _) declNode = nodeForNameExpr n >>= addEdge declNode


nodeForUniform :: NameId -> NameId -> DFGState Node
nodeForUniform n m = do
  DFG{..} <- get
  let i = toUniformId (n, m)
  case getDecl NsU i decls of
    Nothing -> error $ "no node for " <> showUniformId i
    Just ok -> return ok

nodeForNameExpr :: NameExpr -> DFGState Node
nodeForNameExpr (NameExpr n)      = nodeForName n
nodeForNameExpr (UniformExpr n m) = nodeForUniform n m

nodeForName :: Name -> DFGState Node
nodeForName (Name ns n) = nodeFor ns n

nodeFor :: Namespace -> NameId -> DFGState Node
nodeFor ns n = do
  DFG{..} <- get
  case getDecl ns n decls of
    Nothing -> error $ "no node for " <> pp ppName (Name ns n)
    Just ok -> return ok

addEdge :: Node -> Node -> DFGState ()
addEdge declNode idNode =
  modify' $ \g@DFG{gr} -> g { gr = G.insEdge (declNode, idNode, DFGEdge) gr }

addNode :: Namespace -> NameId -> DFGState Node
addNode ns n = do
  g@DFG{..} <- get
  let nodeId = nextNodeId
  put g { gr = G.insNode (nodeId, DFGNode ns n) gr
        , decls = addDecl ns n nodeId decls
        , nextNodeId = nextNodeId + 1
        }
  return nodeId

--------------------------------------------------------------------------------
-- Visualisation
--------------------------------------------------------------------------------

instance GV.Labellable DFGNode where
  toLabelValue (DFGNode NsU i) = GV.toLabelValue $ showUniformId i
  toLabelValue (DFGNode ns n) = GV.toLabelValue $
    pp ppNamespace ns <> show n

instance GV.Labellable DFGEdge where
  toLabelValue DFGEdge = GV.toLabelValue ""

toDot :: FilePath -> DFG -> IO ()
toDot path =
  IO.writeFile path . GV.printIt . GV.graphToDot GV.quickParams . gr

toSvg :: DFG -> FilePath -> IO FilePath
toSvg DFG{gr} = GV.runGraphviz (GV.graphToDot GV.quickParams gr) GV.Svg