module Graphics.Rendering.Ombra.Shader.GLSL (
compileVertexShader,
compileFragmentShader,
uniformName
) where
import Control.Arrow
import Data.List (find)
import Data.Hashable (hash)
import qualified Data.HashMap.Strict as H
import Data.Typeable
import Graphics.Rendering.Ombra.Shader
import Graphics.Rendering.Ombra.Shader.Language.Types
import Graphics.Rendering.Ombra.Shader.Types
import Text.Printf
compileVertexShader :: (ShaderInput i, ShaderInput o)
=> VertexShader i (GVec4, o)
-> (String, (UniformID, [(String, Int)]))
compileVertexShader s = compileShader True header ["gl_Position"] 0 exprShader
where exprShader = s >>^ \(pos, outs) -> ([toExpr pos], outExprs outs)
outExprs = foldrMST (\x l -> (typeName x, toExpr x) : l) []
header = "#version 100\nprecision mediump float;"
compileFragmentShader :: (ShaderInput i, FragmentShaderOutput o)
=> UniformID
-> FragmentShader i o
-> String
compileFragmentShader i s = fst $ compileShader False header outs i exprShader
where exprShader = s >>^ flip (,) [] . map toExpr . toGVec4s
header = concat [ "#version 100\n"
, ext "GL_EXT_draw_buffers"
, ext "GL_OES_standard_derivatives"
, "precision mediump float;"
]
ext e = "#extension " ++ e ++ " : enable\n"
outs = map (\n -> "gl_FragData[" ++ show n ++ "]") [0 .. 15]
compileShader :: ShaderInput i
=> Bool
-> String
-> [String]
-> UniformID
-> Shader s i ([Expr], [(String, Expr)])
-> (String, (UniformID, [(String, Int)]))
compileShader useAttributes header outNames uniformID (Shader shaderFun _) =
let inputFun | useAttributes = Attribute
| otherwise = Input
(input, _) = buildMST' (\t -> fromExpr . inputFun t) 0
inputTypes = foldrMST (\x -> (typeName x :)) [] input
((ShaderState uniformID' uniMap _), (outs, outVaryings)) =
shaderFun (ShaderState uniformID [] [], input)
(outVaryingTypes, outVaryingExprs) = unzip outVaryings
showVar qual ty nm = concat [qual, " ", ty', " ", nm', ";"]
where (ty', arr) = break (== '[') ty
nm' = nm ++ arr
showVars qual name vars = concatMap (\(ty, n) -> showVar qual ty
(name n))
(zip vars [0 ..])
uniType (UniformValue (_ :: Proxy g) _) = typeName (undefined :: g)
uniType (UniformTexture _) = typeName (undefined :: GSampler2D)
uniVars = concatMap (\(uid, val) -> showVar "uniform"
(uniType val)
(uniformName uid))
uniMap
inputVars | useAttributes = showVars "attribute" attributeName
inputTypes
| otherwise = showVars "varying" varyingName inputTypes
outVaryingVars = showVars "varying" varyingName outVaryingTypes
outVaryingNames = map varyingName [0 ..]
(compiledActions, outStrs) = compile (outs ++ outVaryingExprs)
compiledOuts = zipWith (\n s -> showString (n ++ "=") . s .
showString ";")
(outNames ++ outVaryingNames)
outStrs
in ( concat [ header
, uniVars
, inputVars
, outVaryingVars
, "void main(){"
, compiledActions ""
, foldr (.) id compiledOuts $ ""
, "}"
]
, ( uniformID'
, if useAttributes
then zipWith (\size n -> (attributeName n, size))
(foldrMST (\x -> (size x :)) [] input)
[0 ..]
else []
)
)
type ActionID = Int
type ActionMap = H.HashMap ActionID Action
type ActionSet = H.HashMap ActionID ()
data ActionInfo = ActionInfo {
actionGenerator :: ActionGenerator,
actionDeps :: ActionSet,
actionContext :: ActionContext
}
type ActionGenerator = ShowS -> ShowS
data ActionContext = ShallowContext ActionSet
| DeepContext ActionSet
deriving (Show)
type ActionGraph = H.HashMap ActionID ActionInfo
compile :: [Expr] -> (ShowS, [ShowS])
compile exprs = let exprs' = optimize 1 exprs
(strs, deps, _) = unzip3 $ map compileExpr exprs'
depGraph = contextAll deep . buildActionGraph $ H.unions deps
sorted = sortActions depGraph
in (genList sorted, strs)
optimize :: Int
-> [Expr]
-> [Expr]
optimize n exprs = let reps fullExprMap expr (optimizedExprs, exprMap) =
let h = hash expr
exprMap' = H.insertWith (+) h n exprMap
sub = subExprs expr
(optimizedSubExprs, exprMap'') =
foldr (reps fullExprMap)
([], exprMap')
sub
expr' = replaceSubExprs expr optimizedSubExprs
storedExpr = case exprType expr' of
Just t -> Action (Store t expr') 0
Nothing -> expr'
optimizedExpr | length sub < 1 = expr'
| fullExprMap H.! h > 1 = storedExpr
| otherwise = expr'
optimizedExprs' = optimizedExpr : optimizedExprs
in (optimizedExprs', exprMap'')
(optimizedExprs, fullExprMap) =
foldr (reps fullExprMap) ([], H.empty) exprs
in optimizedExprs
generate :: ActionGenerator -> ActionGraph -> ShowS
generate gen graph = gen . genList $ sortActions graph
genList :: [(ActionGenerator, ActionGraph)] -> ShowS
genList = foldr (.) id . map (uncurry generate)
sortActions :: ActionGraph -> [(ActionGenerator, ActionGraph)]
sortActions fullGraph = visitLoop (H.empty, [], fullGraph)
where visitLoop state@(childrenMap, sortedIDs, graph)
| H.null graph = map (makePair childrenMap fullGraph) sortedIDs
| otherwise = visitLoop $ visit (head $ H.keys graph) state
visit aID state@(_, _, graph) =
case H.lookup aID graph of
Nothing -> state
Just ai -> visitNew aID ai state
visitNew aID ai (childrenMap, sortedIDs, graph) =
let deps = actionDeps ai
(childrenMap', sortedIDs', graph') =
H.foldrWithKey
(\aID' _ state -> visit aID' state)
(childrenMap, sortedIDs, graph)
deps
in case actionContext ai of
DeepContext ctx | H.null ctx ||
ctx == H.singleton aID () ->
( childrenMap', sortedIDs' ++ [aID]
, H.delete aID graph' )
DeepContext ctx ->
let smap = H.map (\_ -> H.singleton aID ai
) ctx
cmap' = H.unionWith H.union smap
childrenMap'
in (cmap', sortedIDs', H.delete aID graph')
ShallowContext _ ->
error "sortActions: unexpected \
\ShallowContext"
makePair childrenMap graph aID =
( actionGenerator $ graph H.! aID
, case H.lookup aID childrenMap of
Just g -> H.map (delDeep aID) g
Nothing -> H.empty )
delDeep k ai = let (DeepContext ctx) = actionContext ai
in ai { actionContext = DeepContext $
H.delete k ctx }
buildActionGraph :: ActionMap -> ActionGraph
buildActionGraph = flip H.foldrWithKey H.empty $
\aID act graph ->
let (info, deps) = compileAction aID act
in H.union (H.insert aID info graph)
(buildActionGraph deps)
contextAll :: (ActionID -> ActionGraph -> (ActionContext, ActionGraph))
-> ActionGraph -> ActionGraph
contextAll f g = H.foldrWithKey (\aID _ graph -> snd $ f aID graph) g g
deep :: ActionID -> ActionGraph -> (ActionContext, ActionGraph)
deep aID graph =
case actionContext act of
ShallowContext sctx ->
let (dctx, graph') = H.foldrWithKey addDepContext
(sctx, graph)
(actionDeps act)
ctx' = DeepContext dctx
in (ctx', H.insert
aID (act { actionContext = ctx' }) graph')
ctx -> (ctx, graph)
where act = graph H.! aID
addDepContext depID _ (ctx, graph) =
let (DeepContext dCtx, graph') = deep depID graph
in (H.union ctx (H.delete depID dCtx), graph')
compileExpr :: Expr -> (ShowS, ActionMap, ActionSet)
compileExpr Empty = (showString "", H.empty, H.empty)
compileExpr (Read _ s) = (showString s, H.empty, H.empty)
compileExpr (Op1 _ s e) = let left = showString ("(" ++ s)
right = showString ")"
in first3 (\x -> left . x . right) $ compileExpr e
compileExpr (Op2 _ s ex ey) = let (x, ax, cx) = compileExpr ex
(y, ay, cy) = compileExpr ey
str = showString "(" .
x . showString s . y .
showString ")"
in (str, H.union ax ay, H.union cx cy)
compileExpr (Apply _ s es) = let (v : vs, as, cs) = unzip3 $ map compileExpr es
addArg vs v = vs . showString "," . v
args = v . foldl addArg id vs
str = showString (s ++ "(") . args . showString ")"
in (str, H.unions as, H.unions cs)
compileExpr (X e) = first3 (. showString "[0]") $ compileExpr e
compileExpr (Y e) = first3 (. showString "[1]") $ compileExpr e
compileExpr (Z e) = first3 (. showString "[2]") $ compileExpr e
compileExpr (W e) = first3 (. showString "[3]") $ compileExpr e
compileExpr (Literal _ s) = (showString s, H.empty, H.empty)
compileExpr (Input _ i) = (showString $ varyingName i, H.empty, H.empty)
compileExpr (Attribute _ i) = (showString $ attributeName i, H.empty, H.empty)
compileExpr (Uniform _ i) = (showString $ uniformName i, H.empty, H.empty)
compileExpr (Action a n) = let h = hash a
var = showString $ actionName h n
in (var, H.singleton h a, H.empty)
compileExpr (Dummy _) = error "compileExpr: Dummy"
compileExpr (HashDummy _) = error "compileExpr: HashDummy"
compileExpr (ArrayIndex _ eArr ei) = let (arr, aArr, cArr) = compileExpr eArr
(i, ai, ci) = compileExpr ei
str = showString "(" . arr .
showString "[" . i .
showString "])"
in (str, H.union aArr ai, H.union cArr ci)
compileExpr (ContextVar _ i t) = ( showString $ contextVarName t i
, H.empty
, H.singleton i ()
)
compileExprOptimized :: Int -> Expr -> (ShowS, ActionMap, ActionSet)
compileExprOptimized n = compileExpr . head . optimize n . (: [])
first3 :: (a -> a') -> (a, b, c) -> (a', b, c)
first3 f (a, b, c) = (f a, b, c)
compileAction :: ActionID -> Action -> (ActionInfo, ActionMap)
compileAction aID (Store ty expr) =
let (eStr, deps, ctxs) = compileExpr expr
in ( ActionInfo (\c -> c .
showString (ty ++ " " ++ actionName aID 0 ++ "=") .
eStr . showString ";")
(H.map (const ()) deps)
(ShallowContext ctxs)
, deps )
compileAction aID (If cExpr ty tExpr fExpr) =
let (cStr, cDeps, cCtxs) = compileExprOptimized 1 cExpr
(tStr, tDeps, tCtxs) = compileExprOptimized 1 tExpr
(fStr, fDeps, fCtxs) = compileExprOptimized 1 fExpr
deps = H.unions [cDeps, tDeps, fDeps]
name = actionName aID 0
nameS = showString name
in ( ActionInfo (\c -> showString (ty ++ " " ++ name ++ ";if(") .
cStr . showString "){" . c . nameS .
showString "=" . tStr . showString ";}else{" .
nameS . showString "=" . fStr . showString ";}")
(H.map (const ()) deps)
(ShallowContext $ H.unions [cCtxs, tCtxs, fCtxs])
, deps )
compileAction aID (For iters initValuesTypes body) =
let iterNameS = showString $ contextVarName LoopIteration aID
valueNameSs = map (\i -> showString $ contextVarName (LoopValue i)
aID)
[0 ..]
(iterStr, _, _) = compileExpr iters
Just iterType = exprType iters
iterTypeS = showString iterType
(initTypes, initValues) = unzip initValuesTypes
(nExprs, sExpr) = body (ContextVar iterType aID LoopIteration)
(zipWith (\ty i -> ContextVar ty aID
(LoopValue i))
initTypes [0 ..])
(iStrs, iDepss, iCtxss) = unzip3 $ map (compileExprOptimized 1)
initValues
(nStrs, nDepss, nCtxss) = unzip3 $ map (compileExprOptimized 2)
nExprs
(sStr, sDeps, sCtxs) = compileExprOptimized 2 sExpr
deps = H.unions $ sDeps : (iDepss ++ nDepss)
initialization = foldr (\(iStr, ty, valueNameS) str ->
showString (ty ++ " ") . valueNameS .
showString "=" . iStr .
showString ";" . str
)
(showString "")
(zip3 iStrs initTypes valueNameSs)
update = foldr (\(valueNameS, nStr) str ->
valueNameS . showString "=" .
nStr . showString ";" . str
)
(showString "")
(zip valueNameSs nStrs)
in ( ActionInfo (\c -> initialization .
showString "for(" . iterTypeS . showString " " .
iterNameS . showString "=0;" . iterNameS .
showString "<" . iterStr .
showString ";++" . iterNameS .
showString "){" . c . showString "if(" . sStr .
showString "){break;}" . update .
showString "}")
(H.map (const ()) deps)
(ShallowContext . H.unions $ sCtxs : (iCtxss ++ nCtxss))
, deps )
actionName :: ActionID -> Int -> String
actionName aID i = concat ["a", hashName aID ++ "_" ++ show i]
contextVarName :: ContextVarType -> ActionID -> String
contextVarName LoopIteration = ('l' :) . hashName
contextVarName (LoopValue i) = flip actionName i
hashName :: ActionID -> String
hashName = printf "%x"
uniformName :: Int -> String
uniformName = ('u' :) . show
varyingName :: Int -> String
varyingName = ('v' :) . show
attributeName :: Int -> String
attributeName = ('t' :) . show