packages feed

GPipe-Core-0.2.3.1: src/Graphics/GPipe/Internal/TransformFeedback.hs

{-# LANGUAGE Arrows                     #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE ScopedTypeVariables        #-}

module Graphics.GPipe.Internal.TransformFeedback where

import           Data.Text.Lazy                          (Text)
import qualified Data.Text.Lazy                          as LT
import           Graphics.GPipe.Internal.Buffer          (Buffer (bufName, bufTransformFeedback))
import           Graphics.GPipe.Internal.Compiler        (Drawcall (Drawcall),
                                                          RenderIOState (transformFeedbackToRenderIO))
import           Graphics.GPipe.Internal.Context         (Window (getWinName))
import           Graphics.GPipe.Internal.Expr            (ExprM,
                                                          ExprResult (ExprResult),
                                                          GGenerativeGeometry,
                                                          GlobDeclM, S (..),
                                                          declareGeometryLayout,
                                                          runExprM)
import           Graphics.GPipe.Internal.GeometryStream  (GeometryExplosive (declareGeometry, enumerateVaryings),
                                                          GeometryStream (..),
                                                          GeometryStreamData (..))
import           Graphics.GPipe.Internal.PrimitiveArray  (PrimitiveTopology (toGeometryShaderOutputTopology, toLayoutOut))
import           Graphics.GPipe.Internal.PrimitiveStream (PrimitiveStreamData (PrimitiveStreamData),
                                                          VertexInput (VertexFormat))
import           Graphics.GPipe.Internal.Shader          (Shader (..), ShaderM,
                                                          modifyRenderIO,
                                                          tellDrawcall)

import           Graphics.GL.Core45
import           Graphics.GL.Types                       (GLuint)

import           Control.Monad.Trans.State               (evalState)
import           Data.IORef                              (readIORef, writeIORef)
import           Data.IntMap.Lazy                        (insert)
import           Foreign.C.String                        (newCString)
import           Foreign.Marshal                         (alloca, free,
                                                          withArray)
import           Foreign.Storable                        (Storable (peek))

drawNothing :: forall p a s c ds os f. (PrimitiveTopology p, VertexInput a, GeometryExplosive (VertexFormat a))
    => Window os c ds
    -- Output feedback buffers should remain black boxes until synchronized
    -- which won't be necessary when using glDrawTransformFeedback (add a flag
    -- for it?).
    -> (s -> Buffer os a)
    -- maxVertices
    -> Int
    -- We should use a primitive (vertex) stream too, but the way we deal
    -- currently with modular stages is not flexible enough and we stick with
    -- geometry stream.
    -> GeometryStream (GGenerativeGeometry p (VertexFormat a))
    -> Shader os s ()
drawNothing w getTransformFeedbackBuffer maxVertices gs = Shader $ tellDrawcalls w gs getTransformFeedbackBuffer maxVertices

tellDrawcalls :: forall p a s c ds os. (PrimitiveTopology p, VertexInput a, GeometryExplosive (VertexFormat a))
    => Window os c ds
    -> GeometryStream (GGenerativeGeometry p (VertexFormat a))
    -> (s -> Buffer os a)
    -> Int
    -> ShaderM s ()
tellDrawcalls w (GeometryStream xs) getTransformFeedbackBuffer maxVertices =  mapM_ f xs where
    f (x, gsd@(GeometryStreamData n layoutName _)) = do

        let shaderDeclarations = evalState (declareGeometry (undefined :: VertexFormat a)) 0
            varyings = evalState (enumerateVaryings (undefined :: VertexFormat a)) 0
            varyingCount = length varyings
            bufferMode = GL_INTERLEAVED_ATTRIBS
            io s pName = do
                names <- mapM (newCString . LT.unpack) varyings
                withArray names $ \a -> do
                    glTransformFeedbackVaryings pName (fromIntegral varyingCount) a bufferMode
                mapM_ free names
            topology = toGeometryShaderOutputTopology (undefined :: p)

        tellDrawcall $ makeDrawcall w getTransformFeedbackBuffer topology gsd shaderDeclarations $ do
            declareGeometryLayout layoutName (toLayoutOut (undefined :: p)) maxVertices
            x' <- unS x
            return ()

        modifyRenderIO (\s -> s { transformFeedbackToRenderIO = insert n io (transformFeedbackToRenderIO s) } )

makeDrawcall :: forall a s c ds os. (VertexInput a)
    => Window os c ds
    -> (s -> Buffer os a)
    -> GLuint
    -> GeometryStreamData
    -> GlobDeclM ()
    -> ExprM ()
    -> IO (Drawcall s)
makeDrawcall w getTransformFeedbackBuffer topology (GeometryStreamData geoN _ (PrimitiveStreamData primN ubuff)) shaderDeclarations shader = do
    ExprResult gsource gunis gsamps _ prevShaderDeclarations prevShader <- runExprM shaderDeclarations shader
    ExprResult vsource vunis vsamps vinps _ _ <- runExprM prevShaderDeclarations prevShader

    let getTransformFeedbackBufferName = \s -> do
            let buffer = getTransformFeedbackBuffer s
            bName <- readIORef (bufName buffer)
            let tfRef = bufTransformFeedback buffer
            tf <- readIORef tfRef
            (tfName, tfqName) <- case tf of
                Just names -> return names
                Nothing -> do
                    tfName <- alloca $ \ptr -> do
                        glGenTransformFeedbacks 1 ptr
                        peek ptr
                    tfqName <- alloca $ \ptr -> do
                        glGenQueries 1 ptr
                        peek ptr
                    writeIORef tfRef (Just (tfName, tfqName))
                    --liftNonWinContextIO $ (addContextFinalizer tfRef $ with name (glDeleteTransformFeedbacks 1))
                    return (tfName, tfqName)
            return (bName, tfName, tfqName, topology)

    return $ Drawcall
        (const (Left (getWinName w), return ()))
        (Just getTransformFeedbackBufferName)
        primN
        (Just geoN)
        vsource (Just gsource) Nothing
        vinps
        vunis vsamps
        gunis gsamps
        [] []
        ubuff