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 () }