Files
loop/src/Shader/Compile.hs
T
2023-04-13 00:10:43 +01:00

376 lines
12 KiB
Haskell

module Shader.Compile (
setupVBO,
setupVBOStatic,
makeShaderEBO,
makeShaderVBO,
makeShaderUsingVBO,
makeByteStringShaderUsingVAO,
makeShaderFixedVertices,
makeShaderUsingVAO,
setupVAOUsingVBO,
setupVAOvbo,
setupEBO,
setupVertexAttribPointer,
makeSourcedShader,
toFloatVAs,
setupStaticVBOVAO
) where
import Foreign.C.Types
import Graphics.GL.Types
import Control.Lens
import Control.Monad
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BU
import Foreign
import Foreign.C.String
import GLHelp
import Graphics.GL.Core45
import Shader.Data
import Shader.Parameters
--import Graphics.GL.Core45
{- |
Compiles a full shader found within the shader directory.
The shader is made up of files begining with the inputted string with extensions .vert, .geom etc.
-}
makeShaderVBO ::
-- | First part of the name of the shader
String ->
-- | shader types
[GLenum] ->
-- | The input vertex sizes
[Int] ->
PrimitiveMode ->
IO (Shader, VBO)
makeShaderVBO s shaderlist sizes pm = do
prog <- makeSourcedShader s shaderlist
(vao, vbo) <- setupVBOVAO sizes
return
( Shader
{ _shaderUINT = prog
, _shaderVAO = vao
, _shaderPrimitive = pm
}
, vbo
)
makeShaderEBO ::
-- | First part of the name of the shader
String ->
-- | shader types
[GLenum] ->
-- | The input vertex sizes
[Int] ->
PrimitiveMode ->
VBO ->
IO (Shader, EBO)
makeShaderEBO s shaderlist sizes pm vbo = do
shad <- makeShaderUsingVBO s shaderlist (toFloatVAs sizes) pm vbo
ebo <- setupEBO (shad ^. shaderVAO)
glVertexArrayElementBuffer (shad ^. shaderVAO . vaoName) (ebo ^. eboName)
return
( shad
, ebo
)
makeShaderUsingVBO ::
-- | First part of the name of the shader
String ->
-- | shader types
[GLenum] ->
-- | The input vertex sizes
[VertexAttribute] ->
PrimitiveMode ->
VBO ->
IO Shader
makeShaderUsingVBO s shaderlist sizes pm vbo = do
vao <- setupVAOUsingVBO sizes vbo
makeShaderUsingVAO s shaderlist pm vao
setupVBO :: Int -> IO VBO
setupVBO vertexsize = do
vboname <- mglCreate glCreateBuffers
thePtr <- mallocArray (vertexsize * numDrawableElements)
-- Allocate space
glNamedBufferData
vboname
(fromIntegral $ floatSize * numDrawableElements * vertexsize)
nullPtr
GL_STREAM_DRAW
return VBO{_vboName = vboname, _vboPtr = thePtr, _vboVertexBytes = floatSize * vertexsize}
-- the input ptr is assumed to contain the correct amount of data according to
-- the specified number and type of vertices
-- note the VBO here does not have a sensible ptr value
setupStaticVBOVAO :: Storable a => [VertexAttribute] -> [a] -> IO (VBO,VAO)
setupStaticVBOVAO vas vdata = withArrayLen vdata $ \i ptr -> do
vboname <- mglCreate glCreateBuffers
glNamedBufferStorage
vboname
(CPtrdiff (fromIntegral (i * sizeOf (head vdata))))
ptr
0
let vbo = VBO
{ _vboName = vboname
, _vboPtr = nullPtr
, _vboVertexBytes = vasTightStride vas
}
vao <- setupVAOUsingVBO vas vbo
return (vbo,vao)
setupVBOStatic :: Int -> IO VBO
setupVBOStatic vertexsize = do
vboname <- mglCreate glCreateBuffers
thePtr <- mallocArray (vertexsize * numDrawableElements)
-- Allocate space
glNamedBufferData
vboname
(fromIntegral $ floatSize * numDrawableElements * vertexsize)
nullPtr
GL_STATIC_DRAW
return VBO{_vboName = vboname, _vboPtr = thePtr, _vboVertexBytes = floatSize * vertexsize}
makeByteStringShaderUsingVAO ::
-- | (Arbitrary) name of the shader
String ->
-- | Filetype extensions and shader data
[(GLenum, BS.ByteString)] ->
PrimitiveMode ->
VAO ->
IO Shader
makeByteStringShaderUsingVAO s shaderlist pm vao = do
prog <- makeShaderProgram s shaderlist
return
Shader
{ _shaderUINT = prog
, _shaderVAO = vao
, _shaderPrimitive = pm
}
-- | Takes the VAO from elsewhere
makeShaderUsingVAO ::
-- | First part of the name of the shader
String ->
-- | shader types
[GLenum] ->
PrimitiveMode ->
VAO ->
IO Shader
makeShaderUsingVAO s shaderlist pm theVAO = do
prog <- makeSourcedShader s shaderlist
return
Shader
{ _shaderUINT = prog
, _shaderVAO = theVAO
, _shaderPrimitive = pm
}
-- this should be changed to some FIXED STORAGE SHADER
-- I cannot think of any other reason to have it
{- |
Compiles a full shader found within the shader directory.
The shader is made up of files begining with the inputted string with extensions .vert, .geom etc.
-}
makeShaderFixedVertices ::
-- | First part of the name of the shader
String ->
-- | shader types
[GLenum] ->
-- | The input vertex sizes
[Int] ->
-- | Number of vertexes that can be poked
Int ->
PrimitiveMode ->
IO (Shader, VBO)
makeShaderFixedVertices s shaderlist sizes ndraw pm = do
prog <- makeSourcedShader s shaderlist
(vao, vbo) <- setupVBOVAO sizes
return
( Shader
{ _shaderUINT = prog
, _shaderVAO = vao
, _shaderPrimitive = pm
}
, vbo
)
{- | Compile shader and get its uniform locations.
supposes the shader code is in the shader folder, with the string names
followed by .vert/.geom/.frag.
-}
makeSourcedShader :: String -> [GLenum] -> IO GLuint
makeSourcedShader s sts = do
sources <- forM sts $ \st -> BS.readFile ("shader/" ++ s ++ shaderTypeExt st)
makeShaderProgram s $ zip sts sources
shaderTypeExt :: GLenum -> String
shaderTypeExt GL_VERTEX_SHADER = ".vert"
shaderTypeExt GL_GEOMETRY_SHADER = ".geom"
shaderTypeExt GL_FRAGMENT_SHADER = ".frag"
shaderTypeExt GL_COMPUTE_SHADER = ".comp"
shaderTypeExt _ = undefined
setupVAOvbo :: [VertexAttribute] -> Int -> GLuint -> IO VAO
setupVAOvbo sizes strd vbo = do
vaoname <- mglCreate glCreateVertexArrays
glBindVertexArray vaoname
setupVertexAttribs vbo vaoname sizes (fromIntegral strd * fromIntegral floatSize)
return $
VAO
{ _vaoName = vaoname
}
setupVAOUsingVBO :: [VertexAttribute] -> VBO -> IO VAO
setupVAOUsingVBO vas vbo = do
let strd = vbo ^. vboVertexBytes
vaoname <- mglCreate glCreateVertexArrays
glBindVertexArray vaoname
setupVertexAttribs (vbo ^. vboName) vaoname vas
(fromIntegral strd)
return VAO{_vaoName = vaoname}
setupEBO :: VAO -> IO EBO
setupEBO vao = do
eboptr <- mallocArray numDrawableElements
eboname <- mglCreate glCreateBuffers
glNamedBufferData
eboname
(fromIntegral $ glushortSize * numDrawableElements)
nullPtr
GL_STREAM_DRAW
glVertexArrayElementBuffer (vao ^. vaoName) eboname
return $ EBO eboname eboptr
setupVBOVAO :: [Int] -> IO (VAO, VBO)
setupVBOVAO sizes = do
vaoname <- mglCreate glCreateVertexArrays
glBindVertexArray vaoname
vbo <- setupVBO (sum sizes)
--vao <- setupVAOvbo (toFloatVAs sizes) (sum sizes) (vbo ^. vboName)
vao <- setupVAOUsingVBO (toFloatVAs sizes) vbo
return (vao,vbo)
where
vas = toFloatVAs sizes
strd = vasTightStride vas
toFloatVAs :: [Int] -> [VertexAttribute]
toFloatVAs = go 0
where
go _ [] = []
go x (i:is) = VertexAttribute (fromIntegral i) GL_FLOAT GL_FALSE x
: go (x + fromIntegral floatSize*fromIntegral i) is
setupVertexAttribs :: GLuint -> GLuint -> [VertexAttribute] -> GLsizei -> IO ()
setupVertexAttribs vbo vao vas strd = do
glVertexArrayVertexBuffer vao 0 vbo 0 strd
zipWithM_ (setupVertexAttribPointer vao) [0..] vas
-- | Assumes the correct VBO is bound
setupVertexAttribPointer ::
GLuint ->
-- | Location (in shader)
Int ->
VertexAttribute ->
IO ()
setupVertexAttribPointer vao loc va = do
glEnableVertexArrayAttrib vao loc'
glVertexArrayAttribFormat vao loc'
(va ^. vaCount)
(va ^. vaType)
(va ^. vaNormalize)
(va ^. vaOffset)
glVertexArrayAttribBinding vao loc' 0
where
loc' = fromIntegral loc
makeShaderProgram ::
String ->
[(GLenum, BS.ByteString)] -> -- list of shaders
IO GLuint
makeShaderProgram str srcs = do
theprog <- glCreateProgram
shaders <- mapM (compileAndCheckShader str) srcs
mapM_ (glAttachShader theprog) shaders
glLinkProgram theprog
checkErrorGL (str ++ " linking ") glGetProgramiv glGetProgramInfoLog theprog GL_LINK_STATUS
mapM_ (glDetachShader theprog) shaders
mapM_ glDeleteShader shaders
return theprog
checkErrorGL ::
(Storable t1) =>
[Char] ->
(t2 -> GLenum -> Ptr t1 -> IO ()) ->
(t2 -> t1 -> Ptr a3 -> CString -> IO ()) ->
t2 ->
GLenum ->
IO ()
checkErrorGL str f g x statustype =
alloca $ \statusPtr -> do
f x statustype statusPtr
status <- peek $ castPtr statusPtr
when (status == GL_FALSE) $
alloca $ \ptr -> do
f x GL_INFO_LOG_LENGTH ptr
len <- peek ptr -- we may have to use this length more intelligently
alloca $ \charPtr -> do
g x len nullPtr charPtr
char <- peekCString charPtr
error $ str ++ show char
-- use glCreateShaderProgramv here
compileAndCheckShader :: String -> (GLenum, BS.ByteString) -> IO GLuint
compileAndCheckShader str (theShaderType, sourceCode) = do
theShader <- glCreateShader theShaderType
setShaderSource theShader sourceCode
glCompileShader theShader
checkErrorGL
(str ++ shaderTypeExt theShaderType)
glGetShaderiv
glGetShaderInfoLog
theShader
GL_COMPILE_STATUS
return theShader
setShaderSource :: GLuint -> BS.ByteString -> IO ()
setShaderSource si src =
withByteString src $ \srcPtr srcLength ->
with srcPtr $ \srcPtrBuf ->
with srcLength $ \srcLengthBuf ->
glShaderSource si 1 srcPtrBuf srcLengthBuf
vasTightStride :: [VertexAttribute] -> Int
vasTightStride = go 0
where
go x [] = x
go x (v:vs)
| x /= fromIntegral (v ^. vaOffset) = error "vasTightStride: vertex offset incorrect"
| otherwise = go (x + fromIntegral (v ^. vaCount * fromIntegral (attribSize (v ^. vaType))))
vs
attribSize :: GLenum -> Int
attribSize x = case x of
GL_BYTE -> sizeOf (0 :: GLbyte )
GL_SHORT -> sizeOf (0 :: GLshort)
GL_INT -> sizeOf (0 :: GLint)
GL_FIXED -> sizeOf (0 :: GLfixed)
GL_FLOAT -> sizeOf (0 :: GLfloat)
GL_HALF_FLOAT -> sizeOf (0 :: GLhalf)
GL_DOUBLE -> sizeOf (0 :: GLdouble)
GL_UNSIGNED_BYTE -> sizeOf (0 :: GLubyte)
GL_UNSIGNED_SHORT -> sizeOf (0 :: GLushort)
GL_UNSIGNED_INT -> sizeOf (0 :: GLuint)
-- GL_INT_2_10_10_10_REV -> sizeOf (0 :: GL_INT_2_10_10_10_REV)
-- GL_UNSIGNED_INT_2_10_10_10_REV -> sizeOf (0 :: GL_UNSIGNED_INT_2_10_10_10_REV)
-- GL_UNSIGNED_INT_10F_11F_11F_REV -> sizeOf (0 :: GL_UNSIGNED_INT_10F_11F_11F_REV)
-- https://hackage.haskell.org/package/OpenGL-3.0.3.0/docs/src/Graphics.Rendering.OpenGL.GL.ByteString.html#withByteStringP
withByteString :: Num t => BS.ByteString -> (Ptr b -> t -> IO a) -> IO a
withByteString bs act =
BU.unsafeUseAsCStringLen bs $ \(ptr, size) ->
act (castPtr ptr) (fromIntegral size)