lambdacube-engine-0.1.1: Graphics/LambdaCube/RenderSystem/GL/GLIndexBuffer.hs
module Graphics.LambdaCube.RenderSystem.GL.GLIndexBuffer 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.HardwareIndexBuffer
import Graphics.LambdaCube.RenderSystem.GL.GLUtils
-- | Specialisation of HardwareIndexBuffer for GL
data GLIndexBuffer
= GLIndexBuffer
{ glibType :: IndexType
, glibNumIndexes :: Int
, glibUsage :: Usage
, glibLockInfo :: IORef (Maybe (Int,Int,Bool)) -- LockStart LockSize RequireUpdate
, glibShadowBuffer :: Maybe (Ptr Word8)
, glibBufferObject :: Maybe GL.BufferObject
}
deriving Eq
instance HardwareIndexBuffer GLIndexBuffer where
getIndexType = glibType
getNumIndexes = glibNumIndexes
getIndexSize b = case getIndexType b of
{ IT_16BIT -> 2
; IT_32BIT -> 4
}
instance HardwareBuffer GLIndexBuffer where
getSizeInBytes b = getIndexSize b * glibNumIndexes b
isSystemMemory = isNothing . glibBufferObject
hasShadowBuffer b = (isJust $ glibShadowBuffer b) && (isJust $ glibBufferObject b)
getUsage = glibUsage
isLocked = glibIsLocked
unlock = glibUnlock
lock = glibLock
glibIsLocked b = do
i <- readIORef $ glibLockInfo b
return $ isJust i
glibLock a offs len opts = do
lockinfo <- readIORef (glibLockInfo a)
when (isJust lockinfo) $ error "Cannot lock this buffer, it is already locked!"
writeIORef (glibLockInfo a) $ Just (offs,len,opts /= HBL_READ_ONLY)
case glibShadowBuffer a of
{ Just b -> return $ plusPtr b offs
; Nothing -> do
GL.bindBuffer GL.ElementArrayBuffer $= (glibBufferObject a)
-- Discard if required
when (opts == HBL_DISCARD) $ GL.bufferData GL.ElementArrayBuffer $= (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.ElementArrayBuffer access
case mb of
{ Just b -> return $ plusPtr b offs
; Nothing -> error "Index Buffer: Out of memory"
}
}
glibUnlock a = do
lockinfo <- readIORef (glibLockInfo a)
let (lockStart,lockSize,reqUpdate) = case lockinfo of
{ Just li -> li
; Nothing -> error "Cannot unlock this buffer, it is not locked!"
}
writeIORef (glibLockInfo a) Nothing
case glibShadowBuffer a of
{ Just b -> when reqUpdate $ do -- If we used the shadow buffer this time...
GL.bindBuffer GL.ElementArrayBuffer $= (glibBufferObject a)
case lockStart == 0 && lockSize == getSizeInBytes a of
{ True -> GL.bufferData GL.ElementArrayBuffer $= (fromIntegral lockSize, b, getGLUsage $ getUsage a)
; False -> GL.bufferSubData GL.ElementArrayBuffer GL.WriteToBuffer (fromIntegral lockStart) (fromIntegral lockSize) b
}
; Nothing -> do
GL.bindBuffer GL.ElementArrayBuffer $= (glibBufferObject a)
ok <- GL.unmapBuffer GL.ElementArrayBuffer
unless ok $ error "Buffer data corrupted, please reload"
}
--mkGLIndexBuffer :: RenderSystem rs vb _ _ => rs -> IndexType -> Int -> Usage -> Bool -> IO GLIndexBuffer
mkGLIndexBuffer rs itype numIndexes usage useShadowBuffer = do
let isize = case itype of
{ IT_16BIT -> 2
; IT_32BIT -> 4
}
lockinfo <- newIORef Nothing
[bufferObject] <- GL.genObjectNames 1
GL.bindBuffer GL.ElementArrayBuffer $= Just bufferObject
GL.bufferData GL.ElementArrayBuffer $= (fromIntegral $ numIndexes * isize, nullPtr, getGLUsage usage)
shadowBuffer <- case useShadowBuffer of
{ True -> do
b <- mallocBytes $ numIndexes * isize
return $ Just b
; False -> return Nothing
}
return $ GLIndexBuffer
{ glibType = itype
, glibNumIndexes = numIndexes
, glibUsage = usage
, glibLockInfo = lockinfo
, glibShadowBuffer = shadowBuffer
, glibBufferObject = Just bufferObject
}
rmGLIndexBuffer :: GLIndexBuffer -> IO ()
rmGLIndexBuffer a = do
case glibBufferObject a of
{ Just b -> GL.deleteObjectNames [b]
; Nothing -> return ()
}
case glibShadowBuffer a of
{ Just b -> free b
; Nothing -> return ()
}