lambdacube-engine-0.1.1: Graphics/LambdaCube/RenderSystem/GL/GLVertexBuffer.hs
module Graphics.LambdaCube.RenderSystem.GL.GLVertexBuffer where
import Data.Word
import Data.Maybe
import Foreign.Marshal.Alloc
import Foreign.Marshal.Utils
import Foreign.Storable
import Foreign.C.Types
import Foreign.Ptr
import Data.IORef
import Control.Monad
import Graphics.Rendering.OpenGL (($=))
import qualified Graphics.Rendering.OpenGL as GL
import Graphics.LambdaCube.RenderSystem
import Graphics.LambdaCube.HardwareBuffer
import Graphics.LambdaCube.HardwareVertexBuffer
import Graphics.LambdaCube.RenderSystem.GL.GLUtils
-- | Specialisation of HardwareVertexBuffer for GL
data GLVertexBuffer
= GLVertexBuffer
{ glvbNumVertices :: Int
, glvbVertexSize :: Int
, glvbUsage :: Usage
, glvbLockInfo :: IORef (Maybe (Int,Int,Bool)) -- LockStart LockSize RequireUpdate
, glvbShadowBuffer :: Maybe (Ptr Word8)
, glvbBufferObject :: Maybe GL.BufferObject
}
deriving Eq
instance HardwareVertexBuffer GLVertexBuffer where
getVertexSize = glvbVertexSize
getNumVertices = glvbNumVertices
instance HardwareBuffer GLVertexBuffer where
getSizeInBytes b = glvbVertexSize b * glvbNumVertices b
isSystemMemory = isNothing . glvbBufferObject
hasShadowBuffer b = (isJust $ glvbShadowBuffer b) && (isJust $ glvbBufferObject b)
getUsage = glvbUsage
isLocked = glvbIsLocked
unlock = glvbUnlock
lock = glvbLock
glvbIsLocked b = do
i <- readIORef $ glvbLockInfo b
return $ isJust i
glvbLock a offs len opts = do
lockinfo <- readIORef (glvbLockInfo a)
when (isJust lockinfo) $ error "Cannot lock this buffer, it is already locked!"
writeIORef (glvbLockInfo a) $ Just (offs,len,opts /= HBL_READ_ONLY)
case glvbShadowBuffer a of
{ Just b -> return $ plusPtr b offs
; Nothing -> do
GL.bindBuffer GL.ArrayBuffer $= (glvbBufferObject a)
-- Discard if required
when (opts == HBL_DISCARD) $ GL.bufferData GL.ArrayBuffer $= (fromIntegral $ getSizeInBytes a, nullPtr, getGLUsage $ getUsage a)
let isWriteOnly = case getUsage a of
{ HBU_WRITE_ONLY -> True
; HBU_STATIC_WRITE_ONLY -> True
; HBU_DYNAMIC_WRITE_ONLY -> True
; HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE -> True
; _ -> False
}
access = case (isWriteOnly,opts == HBL_READ_ONLY) of
{ (False,False) -> GL.ReadWrite
; (False,True) -> GL.ReadOnly
; (True,_) -> GL.WriteOnly
}
mb <- GL.mapBuffer GL.ArrayBuffer access
case mb of
{ Just b -> return $ plusPtr b offs
; Nothing -> error "Vertex Buffer: Out of memory"
}
}
glvbUnlock a = do
lockinfo <- readIORef (glvbLockInfo a)
let (lockStart,lockSize,reqUpdate) = case lockinfo of
{ Just li -> li
; Nothing -> error "Cannot unlock this buffer, it is not locked!"
}
writeIORef (glvbLockInfo a) Nothing
case glvbShadowBuffer a of
{ Just b -> when reqUpdate $ do -- If we used the shadow buffer this time...
GL.bindBuffer GL.ArrayBuffer $= (glvbBufferObject a)
case lockStart == 0 && lockSize == getSizeInBytes a of
{ True -> GL.bufferData GL.ArrayBuffer $= (fromIntegral lockSize, b, getGLUsage $ getUsage a)
; False -> GL.bufferSubData GL.ArrayBuffer GL.WriteToBuffer (fromIntegral lockStart) (fromIntegral lockSize) b
}
; Nothing -> do
GL.bindBuffer GL.ArrayBuffer $= (glvbBufferObject a)
ok <- GL.unmapBuffer GL.ArrayBuffer
unless ok $ error "Buffer data corrupted, please reload"
}
--mkGLVertexBuffer :: RenderSystem rs => rs -> Int -> Int -> Usage -> Bool -> IO GLVertexBuffer
mkGLVertexBuffer rs vertexSize numVerts usage useShadowBuffer = do
lockinfo <- newIORef Nothing
[bufferObject] <- GL.genObjectNames 1
GL.bindBuffer GL.ArrayBuffer $= Just bufferObject
GL.bufferData GL.ArrayBuffer $= (fromIntegral $ numVerts * vertexSize, nullPtr, getGLUsage usage)
shadowBuffer <- case useShadowBuffer of
{ True -> do
b <- mallocBytes $ numVerts * vertexSize
return $ Just b
; False -> return Nothing
}
return $ GLVertexBuffer
{ glvbNumVertices = numVerts
, glvbVertexSize = vertexSize
, glvbUsage = usage
, glvbLockInfo = lockinfo
, glvbShadowBuffer = shadowBuffer
, glvbBufferObject = Just bufferObject
}
rmGLVertexBuffer :: GLVertexBuffer -> IO ()
rmGLVertexBuffer a = do
case glvbBufferObject a of
{ Just b -> GL.deleteObjectNames [b]
; Nothing -> return ()
}
case glvbShadowBuffer a of
{ Just b -> free b
; Nothing -> return ()
}