diff options
Diffstat (limited to 'Spear/Render/Core')
| -rw-r--r-- | Spear/Render/Core/Buffer.hs | 122 | ||||
| -rw-r--r-- | Spear/Render/Core/Constants.hs | 12 | ||||
| -rw-r--r-- | Spear/Render/Core/Geometry.hs | 150 | ||||
| -rw-r--r-- | Spear/Render/Core/Pipeline.hs | 74 | ||||
| -rw-r--r-- | Spear/Render/Core/Shader.hs | 216 | ||||
| -rw-r--r-- | Spear/Render/Core/State.hs | 157 |
6 files changed, 731 insertions, 0 deletions
diff --git a/Spear/Render/Core/Buffer.hs b/Spear/Render/Core/Buffer.hs new file mode 100644 index 0000000..6f1e355 --- /dev/null +++ b/Spear/Render/Core/Buffer.hs | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | module Spear.Render.Core.Buffer | ||
| 2 | ( | ||
| 3 | BufferData(..) | ||
| 4 | , BufferDesc(..) | ||
| 5 | , makeBufferAndView | ||
| 6 | , makeBuffer | ||
| 7 | , deleteBuffer | ||
| 8 | , updateBuffer | ||
| 9 | ) | ||
| 10 | where | ||
| 11 | |||
| 12 | import Spear.Game | ||
| 13 | import Spear.Math.Vector | ||
| 14 | import Spear.Render.Core.State | ||
| 15 | |||
| 16 | import Control.Monad (void) | ||
| 17 | import Data.HashMap as HashMap | ||
| 18 | import Data.Word | ||
| 19 | import Foreign.C.Types | ||
| 20 | import Foreign.Marshal.Alloc | ||
| 21 | import Foreign.Marshal.Array | ||
| 22 | import Foreign.Ptr | ||
| 23 | import Foreign.Storable | ||
| 24 | import Graphics.GL.Core46 | ||
| 25 | import Unsafe.Coerce | ||
| 26 | |||
| 27 | |||
| 28 | data BufferData | ||
| 29 | = BufferDataUntyped (Ptr Word8) GLuint | ||
| 30 | | BufferDataVec2 [Vector2] | ||
| 31 | | BufferDataVec3 [Vector3] | ||
| 32 | | BufferDataFloat [Float] | ||
| 33 | | BufferDataU8 [Word8] | ||
| 34 | | BufferDataU16 [Word16] | ||
| 35 | | BufferUninitialized | ||
| 36 | |||
| 37 | data BufferDesc = BufferDesc | ||
| 38 | { bufferDescUsage :: BufferUsage | ||
| 39 | , bufferDescType :: BufferType | ||
| 40 | , bufferDescData :: BufferData | ||
| 41 | } | ||
| 42 | |||
| 43 | |||
| 44 | makeBufferAndView :: BufferDesc -> Game RenderCoreState (BufferView a) | ||
| 45 | makeBufferAndView desc = do | ||
| 46 | buffer <- makeBuffer desc | ||
| 47 | return BufferView | ||
| 48 | { bufferViewBuffer = buffer | ||
| 49 | , bufferViewOffsetBytes = 0 | ||
| 50 | , bufferViewSizeBytes = bufferDataSizeBytes $ bufferDescData desc | ||
| 51 | , bufferViewStrideBytes = 0 | ||
| 52 | } | ||
| 53 | |||
| 54 | makeBuffer :: BufferDesc -> Game RenderCoreState Buffer | ||
| 55 | makeBuffer (BufferDesc usage bufferType bufferData) = do | ||
| 56 | handle <- gameIO $ alloca $ \ptr -> glGenBuffers 1 ptr >> peek ptr | ||
| 57 | resourceKey <- register $ deleteBuffer' handle | ||
| 58 | let buffer = Buffer handle resourceKey bufferType usage | ||
| 59 | gameIO $ updateBuffer buffer bufferData | ||
| 60 | modifyGameState (\state -> state { | ||
| 61 | buffers = HashMap.insert handle buffer (buffers state) }) | ||
| 62 | return buffer | ||
| 63 | |||
| 64 | deleteBuffer :: Buffer -> Game RenderCoreState () | ||
| 65 | deleteBuffer buffer = do | ||
| 66 | let matches buffer = (==bufferHandle buffer) . bufferHandle | ||
| 67 | modifyGameState (\state -> state { | ||
| 68 | buffers = HashMap.delete (bufferHandle buffer) (buffers state) }) | ||
| 69 | release buffer | ||
| 70 | |||
| 71 | -- TODO: use glBufferSubData for updates. | ||
| 72 | updateBuffer :: Buffer -> BufferData -> IO () | ||
| 73 | updateBuffer buffer bufferData = | ||
| 74 | case bufferData of | ||
| 75 | BufferUninitialized -> return () | ||
| 76 | _ -> do | ||
| 77 | glBindBuffer GL_ARRAY_BUFFER (bufferHandle buffer) | ||
| 78 | uploadData (bufferUsage buffer) bufferData | ||
| 79 | glBindBuffer GL_ARRAY_BUFFER 0 | ||
| 80 | |||
| 81 | -- Private | ||
| 82 | |||
| 83 | deleteBuffer' :: GLuint -> IO () | ||
| 84 | deleteBuffer' handle = alloca $ \ptr -> do | ||
| 85 | poke ptr handle | ||
| 86 | glDeleteBuffers 1 ptr | ||
| 87 | |||
| 88 | uploadData :: BufferUsage -> BufferData -> IO () | ||
| 89 | uploadData usage bufferData = case bufferData of | ||
| 90 | BufferDataUntyped ptr sizeBytes -> do | ||
| 91 | glBufferData GL_ARRAY_BUFFER (fromIntegral sizeBytes) (unsafeCoerce ptr) usage' | ||
| 92 | BufferDataVec2 vec2s -> withArrayLen vec2s $ \numElems ptr -> do | ||
| 93 | let sizeBytes = numElems * sizeOf (undefined :: Vector2) | ||
| 94 | glBufferData GL_ARRAY_BUFFER (fromIntegral sizeBytes) ptr usage' | ||
| 95 | BufferDataVec3 vec3s -> withArrayLen vec3s $ \numElems ptr -> do | ||
| 96 | let sizeBytes = numElems * sizeOf (undefined :: Vector3) | ||
| 97 | glBufferData GL_ARRAY_BUFFER (fromIntegral sizeBytes) ptr usage' | ||
| 98 | BufferDataFloat floats -> withArrayLen floats $ \numElems ptr -> do | ||
| 99 | let sizeBytes = numElems * sizeOf (undefined :: CFloat) | ||
| 100 | glBufferData GL_ARRAY_BUFFER (fromIntegral sizeBytes) ptr usage' | ||
| 101 | BufferDataU8 ints -> withArrayLen ints $ \numElems ptr -> do | ||
| 102 | let sizeBytes = numElems * sizeOf (undefined :: Word8) | ||
| 103 | glBufferData GL_ARRAY_BUFFER (fromIntegral sizeBytes) ptr usage' | ||
| 104 | BufferDataU16 ints -> withArrayLen ints $ \numElems ptr -> do | ||
| 105 | let sizeBytes = numElems * sizeOf (undefined :: Word16) | ||
| 106 | glBufferData GL_ARRAY_BUFFER (fromIntegral sizeBytes) ptr usage' | ||
| 107 | BufferUninitialized -> | ||
| 108 | return () | ||
| 109 | where usage' = toGLUsage usage | ||
| 110 | |||
| 111 | toGLUsage :: BufferUsage -> GLenum | ||
| 112 | toGLUsage BufferStatic = GL_STATIC_DRAW | ||
| 113 | toGLUsage BufferDynamic = GL_DYNAMIC_DRAW | ||
| 114 | |||
| 115 | bufferDataSizeBytes :: BufferData -> GLuint | ||
| 116 | bufferDataSizeBytes bufferData = case bufferData of | ||
| 117 | BufferDataUntyped ptr sizeBytes -> sizeBytes | ||
| 118 | BufferDataVec2 vec2s -> fromIntegral $ length vec2s * sizeOf (undefined :: Vector2) | ||
| 119 | BufferDataVec3 vec3s -> fromIntegral $ length vec3s * sizeOf (undefined :: Vector3) | ||
| 120 | BufferDataFloat floats -> fromIntegral $ length floats * 4 | ||
| 121 | BufferDataU8 bytes -> fromIntegral $ length bytes | ||
| 122 | BufferDataU16 words -> fromIntegral $ length words * 2 | ||
diff --git a/Spear/Render/Core/Constants.hs b/Spear/Render/Core/Constants.hs new file mode 100644 index 0000000..befd8ed --- /dev/null +++ b/Spear/Render/Core/Constants.hs | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | module Spear.Render.Core.Constants where | ||
| 2 | |||
| 3 | |||
| 4 | import Graphics.GL.Core46 | ||
| 5 | |||
| 6 | |||
| 7 | positionChannel = 0 :: GLuint | ||
| 8 | normalChannel = 1 :: GLuint | ||
| 9 | tangentChannel = 2 :: GLuint | ||
| 10 | texcoordsChannel = 3 :: GLuint | ||
| 11 | jointsChannel = 4 :: GLuint | ||
| 12 | weightsChannel = 5 :: GLuint | ||
diff --git a/Spear/Render/Core/Geometry.hs b/Spear/Render/Core/Geometry.hs new file mode 100644 index 0000000..aa0dfe5 --- /dev/null +++ b/Spear/Render/Core/Geometry.hs | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | module Spear.Render.Core.Geometry | ||
| 2 | ( | ||
| 3 | newGeometryDesc | ||
| 4 | , makeGeometry | ||
| 5 | , deleteGeometry | ||
| 6 | , renderGeometry | ||
| 7 | , setPositions | ||
| 8 | ) | ||
| 9 | where | ||
| 10 | |||
| 11 | |||
| 12 | import Spear.Game | ||
| 13 | import Spear.Math.Vector.Vector3 | ||
| 14 | import Spear.Render.Core.Buffer | ||
| 15 | import Spear.Render.Core.Constants | ||
| 16 | import Spear.Render.Core.State | ||
| 17 | |||
| 18 | import Data.HashMap as HashMap | ||
| 19 | import Data.IORef | ||
| 20 | import Foreign.Marshal.Alloc | ||
| 21 | import Foreign.Storable | ||
| 22 | import Graphics.GL.Core46 | ||
| 23 | import Unsafe.Coerce | ||
| 24 | |||
| 25 | |||
| 26 | newGeometryDesc :: GeometryDesc | ||
| 27 | newGeometryDesc = GeometryDesc | ||
| 28 | { positions = Nothing | ||
| 29 | , normals = Nothing | ||
| 30 | , tangents = Nothing | ||
| 31 | , texcoords = Nothing | ||
| 32 | , joints = Nothing | ||
| 33 | , weights = Nothing | ||
| 34 | , indices = Nothing | ||
| 35 | , numVerts = 0 | ||
| 36 | , numIndices = 0 | ||
| 37 | , primitiveType = Triangles | ||
| 38 | } | ||
| 39 | |||
| 40 | |||
| 41 | makeGeometry :: GeometryDesc -> Game RenderCoreState Geometry | ||
| 42 | makeGeometry desc = do | ||
| 43 | handle <- gameIO $ alloca $ \ptr -> glGenVertexArrays 1 ptr >> peek ptr | ||
| 44 | gameIO $ do | ||
| 45 | glBindVertexArray handle | ||
| 46 | configureVertexAttributes desc | ||
| 47 | glBindVertexArray 0 | ||
| 48 | descRef <- gameIO $ newIORef desc | ||
| 49 | resourceKey <- register $ deleteGeometry' handle | ||
| 50 | let geometry = Geometry handle resourceKey descRef | ||
| 51 | modifyGameState (\state -> state { | ||
| 52 | geometries = HashMap.insert handle geometry (geometries state) }) | ||
| 53 | return geometry | ||
| 54 | |||
| 55 | deleteGeometry :: Geometry -> Game RenderCoreState () | ||
| 56 | deleteGeometry geometry = do | ||
| 57 | modifyGameState (\state -> state { | ||
| 58 | geometries = HashMap.delete (geometryVao geometry) (geometries state) }) | ||
| 59 | release geometry | ||
| 60 | |||
| 61 | renderGeometry :: Geometry -> IO () | ||
| 62 | renderGeometry geometry = do | ||
| 63 | desc <- readIORef (geometryDesc geometry) | ||
| 64 | let mode = toGLPrimitiveType $ primitiveType desc | ||
| 65 | glBindVertexArray (geometryVao geometry) | ||
| 66 | case indices desc of | ||
| 67 | (Just (IndicesU8 view)) -> renderIndexed view mode (numIndices desc) GL_UNSIGNED_BYTE | ||
| 68 | (Just (IndicesU16 view)) -> renderIndexed view mode (numIndices desc) GL_UNSIGNED_SHORT | ||
| 69 | Nothing -> glDrawArrays mode 0 (numVerts desc) | ||
| 70 | glBindVertexArray 0 | ||
| 71 | |||
| 72 | -- Functions for updating dynamic geometry. | ||
| 73 | |||
| 74 | setPositions :: Geometry -> [Vector3] -> IO () | ||
| 75 | setPositions geometry vectors = do | ||
| 76 | desc <- readIORef $ geometryDesc geometry | ||
| 77 | case positions desc of | ||
| 78 | Just (Positions3d view) -> do | ||
| 79 | updateBuffer (bufferViewBuffer view) (BufferDataVec3 vectors) | ||
| 80 | updateGeometry geometry $ \desc -> desc { | ||
| 81 | numVerts = fromIntegral . length $ vectors | ||
| 82 | } | ||
| 83 | _ -> putStrLn "setPositions ERROR" -- TODO: handle gracefully | ||
| 84 | |||
| 85 | -- Private | ||
| 86 | |||
| 87 | deleteGeometry' :: GLenum -> IO () | ||
| 88 | deleteGeometry' handle = alloca $ \ptr -> do | ||
| 89 | poke ptr handle | ||
| 90 | glDeleteVertexArrays 1 ptr | ||
| 91 | |||
| 92 | updateGeometry :: Geometry -> (GeometryDesc -> GeometryDesc) -> IO () | ||
| 93 | updateGeometry geometry update = do | ||
| 94 | desc <- readIORef $ geometryDesc geometry | ||
| 95 | writeIORef (geometryDesc geometry) (update desc) | ||
| 96 | |||
| 97 | renderIndexed :: BufferView a -> GLenum -> GLsizei -> GLenum -> IO () | ||
| 98 | renderIndexed view mode numIndices indexElemSize = do | ||
| 99 | glBindBuffer GL_ELEMENT_ARRAY_BUFFER (bufferHandle . bufferViewBuffer $ view) | ||
| 100 | glDrawElements mode numIndices GL_UNSIGNED_SHORT (unsafeCoerce $ bufferViewOffsetBytes view) | ||
| 101 | glBindBuffer GL_ELEMENT_ARRAY_BUFFER 0 | ||
| 102 | |||
| 103 | configureVertexAttributes :: GeometryDesc -> IO () | ||
| 104 | configureVertexAttributes desc = do | ||
| 105 | case positions desc of | ||
| 106 | Just (Positions2d view) -> configureView view positionChannel 2 GL_FLOAT GL_FALSE | ||
| 107 | Just (Positions3d view) -> configureView view positionChannel 3 GL_FLOAT GL_FALSE | ||
| 108 | Nothing -> return () | ||
| 109 | case normals desc of | ||
| 110 | Just view -> configureView view normalChannel 3 GL_FLOAT GL_FALSE | ||
| 111 | Nothing -> return () | ||
| 112 | case tangents desc of | ||
| 113 | Just view -> configureView view tangentChannel 4 GL_FLOAT GL_FALSE | ||
| 114 | Nothing -> return () | ||
| 115 | case texcoords desc of | ||
| 116 | Just view -> configureView view texcoordsChannel 2 GL_FLOAT GL_FALSE | ||
| 117 | Nothing -> return () | ||
| 118 | case joints desc of | ||
| 119 | Just (JointsU8 view) -> configureView view jointsChannel 4 GL_UNSIGNED_BYTE GL_FALSE | ||
| 120 | Just (JointsU16 view) -> configureView view jointsChannel 4 GL_UNSIGNED_SHORT GL_FALSE | ||
| 121 | Nothing -> return () | ||
| 122 | case weights desc of | ||
| 123 | Just (WeightsU8 view) -> configureView view weightsChannel 4 GL_UNSIGNED_BYTE GL_TRUE | ||
| 124 | Just (WeightsU16 view) -> configureView view weightsChannel 4 GL_UNSIGNED_SHORT GL_TRUE | ||
| 125 | Just (WeightsFloat view) -> configureView view weightsChannel 4 GL_FLOAT GL_FALSE | ||
| 126 | Nothing -> return () | ||
| 127 | |||
| 128 | -- TODO: Add the assertion: | ||
| 129 | -- desc->num_verts <= view->size_bytes / (num_components * component_size_bytes | ||
| 130 | configureView :: BufferView a -> GLuint -> GLint -> GLenum -> GLboolean -> IO () | ||
| 131 | configureView view channel numComponents componentType normalized = do | ||
| 132 | glBindBuffer GL_ARRAY_BUFFER (bufferHandle . bufferViewBuffer $ view) | ||
| 133 | glEnableVertexAttribArray channel | ||
| 134 | let strideBytes = bufferViewStrideBytes view | ||
| 135 | let offsetBytes = unsafeCoerce $ bufferViewOffsetBytes view | ||
| 136 | if (componentType == GL_FLOAT) || (normalized == GL_TRUE) | ||
| 137 | then do | ||
| 138 | glVertexAttribPointer channel numComponents componentType normalized | ||
| 139 | strideBytes offsetBytes | ||
| 140 | else | ||
| 141 | -- TODO: Assert component type | ||
| 142 | glVertexAttribIPointer channel numComponents componentType | ||
| 143 | strideBytes offsetBytes | ||
| 144 | glBindBuffer GL_ARRAY_BUFFER 0 | ||
| 145 | |||
| 146 | toGLPrimitiveType :: PrimitiveType -> GLenum | ||
| 147 | toGLPrimitiveType primitiveType = case primitiveType of | ||
| 148 | Triangles -> GL_TRIANGLES | ||
| 149 | TriangleFan -> GL_TRIANGLE_FAN | ||
| 150 | TriangleStrip -> GL_TRIANGLE_STRIP | ||
diff --git a/Spear/Render/Core/Pipeline.hs b/Spear/Render/Core/Pipeline.hs new file mode 100644 index 0000000..724b391 --- /dev/null +++ b/Spear/Render/Core/Pipeline.hs | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | module Spear.Render.Core.Pipeline | ||
| 2 | ( | ||
| 3 | BufferTarget(..) | ||
| 4 | , clearBuffers | ||
| 5 | , setBlending | ||
| 6 | , setClearColour | ||
| 7 | , setClearDepth | ||
| 8 | , setClearStencil | ||
| 9 | , setCulling | ||
| 10 | , setDepthMask | ||
| 11 | , setPolygonOffset | ||
| 12 | , setViewport | ||
| 13 | ) | ||
| 14 | where | ||
| 15 | |||
| 16 | import Data.Bits ((.|.)) | ||
| 17 | import Data.List (foldl') | ||
| 18 | import Graphics.GL.Core46 | ||
| 19 | |||
| 20 | |||
| 21 | data BufferTarget | ||
| 22 | = ColourBuffer | ||
| 23 | | DepthBuffer | ||
| 24 | | StencilBuffer | ||
| 25 | |||
| 26 | |||
| 27 | clearBuffers :: [BufferTarget] -> IO () | ||
| 28 | clearBuffers = glClear . toBufferBitfield | ||
| 29 | where toBufferBitfield = foldl' (.|.) 0 . (<$>) toGLEnum | ||
| 30 | toGLEnum target = case target of | ||
| 31 | ColourBuffer -> GL_COLOR_BUFFER_BIT | ||
| 32 | DepthBuffer -> GL_DEPTH_BUFFER_BIT | ||
| 33 | StencilBuffer -> GL_STENCIL_BUFFER_BIT | ||
| 34 | |||
| 35 | setBlending :: Bool -> IO () | ||
| 36 | setBlending enable = | ||
| 37 | if enable | ||
| 38 | then glEnable GL_BLEND >> glBlendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA | ||
| 39 | else glDisable GL_BLEND | ||
| 40 | |||
| 41 | setClearColour :: (Float, Float, Float, Float) -> IO () | ||
| 42 | setClearColour (r,g,b,a) = glClearColor r g b a | ||
| 43 | |||
| 44 | setClearDepth :: Double -> IO () | ||
| 45 | setClearDepth = glClearDepth | ||
| 46 | |||
| 47 | setClearStencil :: Int -> IO () | ||
| 48 | setClearStencil = glClearStencil . fromIntegral | ||
| 49 | |||
| 50 | setCulling :: Bool -> IO () | ||
| 51 | setCulling enable = (if enable then glEnable else glDisable) GL_CULL_FACE | ||
| 52 | |||
| 53 | setDepthMask :: Bool -> IO () | ||
| 54 | setDepthMask enable = glDepthMask (if enable then GL_TRUE else GL_FALSE) | ||
| 55 | |||
| 56 | setPolygonOffset :: Float -> Float -> IO () | ||
| 57 | setPolygonOffset scale bias = do | ||
| 58 | glPolygonOffset scale bias | ||
| 59 | if scale /= 0 && bias /= 0 | ||
| 60 | then glEnable GL_POLYGON_OFFSET_FILL | ||
| 61 | else glDisable GL_POLYGON_OFFSET_FILL | ||
| 62 | |||
| 63 | setViewport :: | ||
| 64 | -- | x | ||
| 65 | Int -> | ||
| 66 | -- | y | ||
| 67 | Int -> | ||
| 68 | -- | width | ||
| 69 | Int -> | ||
| 70 | -- | height | ||
| 71 | Int -> | ||
| 72 | IO () | ||
| 73 | setViewport x y width height = | ||
| 74 | glViewport (fromIntegral x) (fromIntegral y) (fromIntegral width) (fromIntegral height) | ||
diff --git a/Spear/Render/Core/Shader.hs b/Spear/Render/Core/Shader.hs new file mode 100644 index 0000000..4ed4430 --- /dev/null +++ b/Spear/Render/Core/Shader.hs | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | module Spear.Render.Core.Shader | ||
| 2 | ( | ||
| 3 | Define(..) | ||
| 4 | , ShaderSource(..) | ||
| 5 | , ShaderDesc(..) | ||
| 6 | , compileShader | ||
| 7 | , compileShaderProgram | ||
| 8 | , deleteShader | ||
| 9 | , deleteShaderProgram | ||
| 10 | , activateShaderProgram | ||
| 11 | , deactivateShaderProgram | ||
| 12 | , setUniform | ||
| 13 | , applyUniforms | ||
| 14 | ) | ||
| 15 | where | ||
| 16 | |||
| 17 | import Spear.Game | ||
| 18 | import Spear.Math.Matrix4 | ||
| 19 | import Spear.Math.Vector | ||
| 20 | import Spear.Render.Core.State | ||
| 21 | |||
| 22 | import Control.Monad (mapM_) | ||
| 23 | import Data.Bits | ||
| 24 | import Data.Hashable | ||
| 25 | import Data.HashMap as HashMap | ||
| 26 | import Data.IORef | ||
| 27 | import Data.List (deleteBy, foldl', intercalate) | ||
| 28 | import Foreign.C.String | ||
| 29 | import Foreign.Marshal.Alloc | ||
| 30 | import Foreign.Marshal.Array | ||
| 31 | import Foreign.Marshal.Utils | ||
| 32 | import Foreign.Ptr | ||
| 33 | import Foreign.Storable | ||
| 34 | import Graphics.GL.Core46 | ||
| 35 | import Unsafe.Coerce | ||
| 36 | |||
| 37 | |||
| 38 | type Define = (String, String) | ||
| 39 | |||
| 40 | data ShaderSource | ||
| 41 | = ShaderFromString String | ||
| 42 | | ShaderFromFile FilePath | ||
| 43 | deriving Show | ||
| 44 | |||
| 45 | data ShaderDesc = ShaderDesc | ||
| 46 | { shaderDescType :: ShaderType | ||
| 47 | , shaderDescSource :: ShaderSource | ||
| 48 | , shaderDescDefines :: [Define] | ||
| 49 | } | ||
| 50 | |||
| 51 | |||
| 52 | compileShader :: ShaderDesc -> Game RenderCoreState Shader | ||
| 53 | compileShader (ShaderDesc shaderType source defines) = do | ||
| 54 | code <- case source of | ||
| 55 | ShaderFromString code -> return code | ||
| 56 | ShaderFromFile file -> gameIO $ readFile file | ||
| 57 | state <- getGameState | ||
| 58 | let shaderHash = hash code -- TODO: Should also include defines. | ||
| 59 | case HashMap.lookup shaderHash (shaders state) of | ||
| 60 | Just shader -> return shader | ||
| 61 | Nothing -> do | ||
| 62 | let definesString = makeDefinesString defines | ||
| 63 | handle <- gameIO $ glCreateShader (toGLShaderType shaderType) | ||
| 64 | gameIO $ withCStringLen code $ \(codeCString, codeLen) -> | ||
| 65 | withCStringLen definesString $ \(definesCString, definesLen) -> | ||
| 66 | withCStringLen header $ \(headerCString, headerLen) -> | ||
| 67 | withArray [headerCString, definesCString, codeCString] $ \strPtrs -> | ||
| 68 | withArray (fromIntegral <$> [headerLen, definesLen, codeLen] :: [GLint]) | ||
| 69 | $ \lengths -> | ||
| 70 | glShaderSource handle 3 strPtrs lengths | ||
| 71 | err <- gameIO $ do | ||
| 72 | glCompileShader handle | ||
| 73 | alloca $ \statusPtr -> do | ||
| 74 | glGetShaderiv handle GL_COMPILE_STATUS statusPtr | ||
| 75 | result <- peek statusPtr | ||
| 76 | case result of | ||
| 77 | 0 -> alloca $ \lenPtr -> do | ||
| 78 | glGetShaderiv handle GL_INFO_LOG_LENGTH lenPtr | ||
| 79 | len <- peek lenPtr | ||
| 80 | case len of | ||
| 81 | 0 -> return $ Just "" | ||
| 82 | _ -> withCString (replicate (fromIntegral len) '\0') $ \logPtr -> do | ||
| 83 | glGetShaderInfoLog handle len nullPtr logPtr | ||
| 84 | Just <$> peekCString logPtr | ||
| 85 | _ -> return Nothing | ||
| 86 | case err of | ||
| 87 | Nothing -> do | ||
| 88 | resourceKey <- register $ deleteShader' handle | ||
| 89 | let shader = Shader handle resourceKey shaderType shaderHash | ||
| 90 | saveGameState $ state { | ||
| 91 | shaders = HashMap.insert shaderHash shader (shaders state) | ||
| 92 | } | ||
| 93 | return shader | ||
| 94 | Just err -> gameError $ | ||
| 95 | "Failed to compile shader: [" ++ show source ++ "]: " ++ err | ||
| 96 | |||
| 97 | compileShaderProgram :: [Shader] -> Game RenderCoreState ShaderProgram | ||
| 98 | compileShaderProgram shaders = do | ||
| 99 | state <- getGameState | ||
| 100 | let programHash = hashShaders shaders | ||
| 101 | case HashMap.lookup programHash (shaderPrograms state) of | ||
| 102 | Just program -> return program | ||
| 103 | Nothing -> do | ||
| 104 | handle <- gameIO glCreateProgram | ||
| 105 | case handle of | ||
| 106 | 0 -> gameError "Failed to create shader program" | ||
| 107 | _ -> do | ||
| 108 | mapM_ (gameIO . glAttachShader handle) (shaderHandle <$> shaders) | ||
| 109 | err <- gameIO $ do | ||
| 110 | glLinkProgram handle | ||
| 111 | alloca $ \statusPtr -> do | ||
| 112 | glGetProgramiv handle GL_LINK_STATUS statusPtr | ||
| 113 | status <- peek statusPtr | ||
| 114 | case status of | ||
| 115 | 0 -> alloca $ \lenPtr -> do | ||
| 116 | glGetShaderiv handle GL_INFO_LOG_LENGTH lenPtr | ||
| 117 | len <- peek lenPtr | ||
| 118 | case len of | ||
| 119 | 0 -> return $ Just "Unknown error" | ||
| 120 | _ -> withCString (replicate (fromIntegral len) '\0') $ \logPtr -> do | ||
| 121 | glGetShaderInfoLog handle len nullPtr logPtr | ||
| 122 | Just <$> peekCString logPtr | ||
| 123 | _ -> return Nothing | ||
| 124 | case err of | ||
| 125 | Nothing -> do | ||
| 126 | resourceKey <- register $ deleteShaderProgram' handle | ||
| 127 | uniforms <- gameIO $ newIORef [] | ||
| 128 | let program = ShaderProgram handle resourceKey programHash uniforms | ||
| 129 | saveGameState $ state { | ||
| 130 | shaderPrograms = HashMap.insert programHash program (shaderPrograms state) | ||
| 131 | } | ||
| 132 | return program | ||
| 133 | Just err -> gameError $ | ||
| 134 | "Failed to compile shader program: " ++ err ++ "; shaders: " ++ | ||
| 135 | intercalate ", " (show . shaderHandle <$> shaders) | ||
| 136 | |||
| 137 | deleteShader :: Shader -> Game RenderCoreState () | ||
| 138 | deleteShader shader = do | ||
| 139 | modifyGameState (\state -> state { | ||
| 140 | shaders = HashMap.delete (shaderHash shader) (shaders state) }) | ||
| 141 | release shader | ||
| 142 | |||
| 143 | deleteShaderProgram :: ShaderProgram -> Game RenderCoreState () | ||
| 144 | deleteShaderProgram program = do | ||
| 145 | modifyGameState (\state -> state { | ||
| 146 | shaderPrograms = HashMap.delete (shaderProgramHash program) (shaderPrograms state)}) | ||
| 147 | release program | ||
| 148 | |||
| 149 | activateShaderProgram :: ShaderProgram -> IO () | ||
| 150 | activateShaderProgram program = do | ||
| 151 | glUseProgram . shaderProgramHandle $ program | ||
| 152 | applyUniforms program | ||
| 153 | |||
| 154 | deactivateShaderProgram :: ShaderProgram -> IO () | ||
| 155 | deactivateShaderProgram _ = glUseProgram 0 | ||
| 156 | |||
| 157 | setUniform :: ShaderUniform -> ShaderProgram -> IO () | ||
| 158 | setUniform uniform program = | ||
| 159 | modifyIORef (shaderProgramUniforms program) (setUniform' . removeUniform) | ||
| 160 | where removeUniform = deleteBy matchesUniform uniform | ||
| 161 | matchesUniform uniform u = uniformName u == uniformName uniform | ||
| 162 | setUniform' = (:) uniform | ||
| 163 | |||
| 164 | applyUniforms :: ShaderProgram -> IO () | ||
| 165 | applyUniforms program = | ||
| 166 | let update (FloatUniform name value) = | ||
| 167 | glGetUniformLocation' handle name >>= | ||
| 168 | \location -> glUniform1f (fromIntegral location) value | ||
| 169 | update (Vec3Uniform name (Vector3 x y z)) = | ||
| 170 | glGetUniformLocation' handle name >>= | ||
| 171 | \location -> glUniform3f (fromIntegral location) x y z | ||
| 172 | update (Vec4Uniform name (Vector4 x y z w)) = | ||
| 173 | glGetUniformLocation' handle name >>= | ||
| 174 | \location -> glUniform4f (fromIntegral location) x y z w | ||
| 175 | update (Mat4Uniform name mat4) = | ||
| 176 | glGetUniformLocation' handle name >>= | ||
| 177 | \location -> with mat4 $ \ptrMat4 -> | ||
| 178 | glUniformMatrix4fv location 1 GL_FALSE (unsafeCoerce ptrMat4) | ||
| 179 | update (Mat4ArrayUniform name mat4s) = | ||
| 180 | glGetUniformLocation' handle name >>= | ||
| 181 | \location -> withArray mat4s $ \ptrMat4s -> | ||
| 182 | glUniformMatrix4fv location (fromIntegral $ length mat4s) GL_FALSE (unsafeCoerce ptrMat4s) | ||
| 183 | handle = shaderProgramHandle program | ||
| 184 | in do | ||
| 185 | uniforms <- readIORef (shaderProgramUniforms program) | ||
| 186 | mapM_ update uniforms | ||
| 187 | writeIORef (shaderProgramUniforms program) [] | ||
| 188 | |||
| 189 | -- Private | ||
| 190 | |||
| 191 | glGetUniformLocation' :: GLuint -> String -> IO GLint | ||
| 192 | glGetUniformLocation' handle name = | ||
| 193 | withCString name $ \nameCStr -> | ||
| 194 | glGetUniformLocation (fromIntegral handle) (unsafeCoerce nameCStr) | ||
| 195 | |||
| 196 | deleteShader' :: GLuint -> IO () | ||
| 197 | deleteShader' = glDeleteShader | ||
| 198 | |||
| 199 | deleteShaderProgram' :: GLuint -> IO () | ||
| 200 | deleteShaderProgram' = glDeleteProgram | ||
| 201 | |||
| 202 | hashShaders :: [Shader] -> Int | ||
| 203 | hashShaders = foldl' hashF 0 | ||
| 204 | where hashF hash shader = (hash `shiftL` 32) .|. fromIntegral (shaderHandle shader) | ||
| 205 | |||
| 206 | toGLShaderType :: ShaderType -> GLenum | ||
| 207 | toGLShaderType VertexShader = GL_VERTEX_SHADER | ||
| 208 | toGLShaderType FragmentShader = GL_FRAGMENT_SHADER | ||
| 209 | toGLShaderType ComputeShader = GL_COMPUTE_SHADER | ||
| 210 | |||
| 211 | makeDefinesString :: [Define] -> String | ||
| 212 | makeDefinesString defines = intercalate "\n" body ++ "\n" | ||
| 213 | where body = (\(name, value) -> "#define " ++ name ++ " " ++ value) <$> defines | ||
| 214 | |||
| 215 | -- Header prepended to all shaders. | ||
| 216 | header = "#version 400 core\n" | ||
diff --git a/Spear/Render/Core/State.hs b/Spear/Render/Core/State.hs new file mode 100644 index 0000000..34b0732 --- /dev/null +++ b/Spear/Render/Core/State.hs | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | module Spear.Render.Core.State where | ||
| 2 | |||
| 3 | import Spear.Game | ||
| 4 | import Spear.Math.Matrix4 | ||
| 5 | import Spear.Math.Vector | ||
| 6 | |||
| 7 | import Data.HashMap as HashMap | ||
| 8 | import Data.IORef | ||
| 9 | import Data.Word | ||
| 10 | import Graphics.GL.Core46 | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | data BufferType | ||
| 15 | = BufferUntyped | ||
| 16 | | Buffer2d | ||
| 17 | | Buffer3d | ||
| 18 | | Buffer4d | ||
| 19 | | BufferFloat | ||
| 20 | | BufferU8 | ||
| 21 | | BufferU16 | ||
| 22 | |||
| 23 | data BufferUsage | ||
| 24 | = BufferStatic | ||
| 25 | | BufferDynamic | ||
| 26 | |||
| 27 | -- | A data buffer (e.g., vertex attributes, indices). | ||
| 28 | data Buffer = Buffer | ||
| 29 | { bufferHandle :: GLuint | ||
| 30 | , bufferResource :: Resource | ||
| 31 | , bufferType :: BufferType | ||
| 32 | , bufferUsage :: BufferUsage | ||
| 33 | } | ||
| 34 | |||
| 35 | -- | A buffer view. | ||
| 36 | data BufferView a = BufferView | ||
| 37 | { bufferViewBuffer :: Buffer | ||
| 38 | , bufferViewOffsetBytes :: GLuint | ||
| 39 | , bufferViewSizeBytes :: GLuint | ||
| 40 | , bufferViewStrideBytes :: GLsizei | ||
| 41 | } | ||
| 42 | |||
| 43 | |||
| 44 | data Positions | ||
| 45 | = Positions2d (BufferView Vector2) | ||
| 46 | | Positions3d (BufferView Vector3) | ||
| 47 | |||
| 48 | data Joints | ||
| 49 | = JointsU8 (BufferView Word8) | ||
| 50 | | JointsU16 (BufferView Word16) | ||
| 51 | |||
| 52 | data Weights | ||
| 53 | = WeightsU8 (BufferView Word8) | ||
| 54 | | WeightsU16 (BufferView Word16) | ||
| 55 | | WeightsFloat (BufferView Float) | ||
| 56 | |||
| 57 | data Indices | ||
| 58 | = IndicesU8 (BufferView Word8) | ||
| 59 | | IndicesU16 (BufferView Word16) | ||
| 60 | |||
| 61 | data PrimitiveType | ||
| 62 | = Triangles | ||
| 63 | | TriangleFan | ||
| 64 | | TriangleStrip | ||
| 65 | |||
| 66 | -- | A geometry descriptor. | ||
| 67 | data GeometryDesc = GeometryDesc | ||
| 68 | { positions :: Maybe Positions -- Convenient for the empty descriptor. | ||
| 69 | , normals :: Maybe (BufferView Vector3) | ||
| 70 | , tangents :: Maybe (BufferView Vector4) | ||
| 71 | , texcoords :: Maybe (BufferView Vector4) | ||
| 72 | , joints :: Maybe Joints | ||
| 73 | , weights :: Maybe Weights | ||
| 74 | , indices :: Maybe Indices | ||
| 75 | , numVerts :: GLsizei | ||
| 76 | , numIndices :: GLsizei | ||
| 77 | , primitiveType :: PrimitiveType | ||
| 78 | } | ||
| 79 | |||
| 80 | -- | A piece of renderable geometry. | ||
| 81 | -- | ||
| 82 | -- Since dynamic geometry can be mutated, the descriptor is stored as an IORef | ||
| 83 | -- so that its state cannot become stale after an update. | ||
| 84 | data Geometry = Geometry | ||
| 85 | { geometryVao :: GLuint | ||
| 86 | , geometryResource :: Resource | ||
| 87 | , geometryDesc :: IORef GeometryDesc | ||
| 88 | } | ||
| 89 | |||
| 90 | |||
| 91 | -- | A shader. | ||
| 92 | data Shader = Shader | ||
| 93 | { shaderHandle :: GLuint | ||
| 94 | , shaderResource :: Resource | ||
| 95 | , shaderType :: ShaderType | ||
| 96 | , shaderHash :: Int | ||
| 97 | } | ||
| 98 | |||
| 99 | data ShaderType | ||
| 100 | = VertexShader | ||
| 101 | | FragmentShader | ||
| 102 | | ComputeShader | ||
| 103 | deriving (Eq, Show) | ||
| 104 | |||
| 105 | -- | A shader uniform. | ||
| 106 | data ShaderUniform | ||
| 107 | = FloatUniform { uniformName :: String, uniformFloat :: Float } | ||
| 108 | | Vec3Uniform { uniformName :: String, uniformVec3 :: Vector3 } | ||
| 109 | | Vec4Uniform { uniformName :: String, uniformVec4 :: Vector4 } | ||
| 110 | | Mat4Uniform { uniformName :: String, uniformMat4 :: Matrix4 } | ||
| 111 | | Mat4ArrayUniform { uniformName :: String, uniformMat4s :: [Matrix4] } | ||
| 112 | |||
| 113 | -- | A shader program. | ||
| 114 | data ShaderProgram = ShaderProgram | ||
| 115 | { shaderProgramHandle :: GLuint | ||
| 116 | , shaderProgramResource :: Resource | ||
| 117 | , shaderProgramHash :: Int | ||
| 118 | -- Dirty set of uniforms that have been set since the last time uniforms were | ||
| 119 | -- applied. OpenGL retains the values of uniforms for a program until the | ||
| 120 | -- program is linked again, so we only need to store the updates here. | ||
| 121 | , shaderProgramUniforms :: IORef [ShaderUniform] | ||
| 122 | } | ||
| 123 | |||
| 124 | |||
| 125 | -- | Core render state. | ||
| 126 | data RenderCoreState = RenderCoreState | ||
| 127 | { buffers :: Map GLuint Buffer | ||
| 128 | , geometries :: Map GLuint Geometry | ||
| 129 | , shaders :: Map ShaderHash Shader | ||
| 130 | , shaderPrograms :: Map ShaderProgramHash ShaderProgram | ||
| 131 | } | ||
| 132 | |||
| 133 | type ShaderHash = Int | ||
| 134 | type ShaderProgramHash = Int | ||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | instance ResourceClass Buffer where | ||
| 139 | getResource = bufferResource | ||
| 140 | |||
| 141 | instance ResourceClass Geometry where | ||
| 142 | getResource = geometryResource | ||
| 143 | |||
| 144 | instance ResourceClass Shader where | ||
| 145 | getResource = shaderResource | ||
| 146 | |||
| 147 | instance ResourceClass ShaderProgram where | ||
| 148 | getResource = shaderProgramResource | ||
| 149 | |||
| 150 | |||
| 151 | newRenderCoreState :: RenderCoreState | ||
| 152 | newRenderCoreState = RenderCoreState | ||
| 153 | { buffers = HashMap.empty | ||
| 154 | , geometries = HashMap.empty | ||
| 155 | , shaders = HashMap.empty | ||
| 156 | , shaderPrograms = HashMap.empty | ||
| 157 | } | ||
