{-# LANGUAGE RecordWildCards #-} module LambdaCube.GL.Util ( queryUniforms, queryStreams, mkUniformSetter, setUniform, setVertexAttrib, compileShader, printProgramLog, glGetShaderiv1, glGetProgramiv1, Buffer(..), ArrayDesc(..), StreamSetter, streamToInputType, arrayTypeToGLType, comparisonFunctionToGLType, logicOperationToGLType, blendEquationToGLType, blendingFactorToGLType, checkGL, textureDataTypeToGLType, textureDataTypeToGLArityType, glGetIntegerv1, setSampler, checkFBO, compileSampler, compileTexture, primitiveToFetchPrimitive, primitiveToGLType, inputTypeToTextureTarget ) where import Control.Applicative import Control.Exception import Control.Monad import Data.IORef import Data.List as L import Foreign import Foreign.C.String import qualified Data.Vector as V import Data.Vector.Unboxed.Mutable (IOVector) import qualified Data.Vector.Unboxed.Mutable as MV import Data.Map (Map) import qualified Data.Map as Map import Graphics.GL.Core33 import LambdaCube.Linear import LambdaCube.IR import LambdaCube.PipelineSchema import LambdaCube.GL.Type setSampler :: GLint -> Int32 -> IO () setSampler i v = glUniform1i i $ fromIntegral v z2 = V2 0 0 :: V2F z3 = V3 0 0 0 :: V3F z4 = V4 0 0 0 0 :: V4F -- uniform functions queryUniforms :: GLuint -> IO (Map String GLint, Map String InputType) queryUniforms po = do ul <- getNameTypeSize po glGetActiveUniform glGetUniformLocation GL_ACTIVE_UNIFORMS GL_ACTIVE_UNIFORM_MAX_LENGTH let uNames = [n | (n,_,_,_) <- ul] uTypes = [fromGLType (e,s) | (_,_,e,s) <- ul] uLocation = [i | (_,i,_,_) <- ul] return $! (Map.fromList $! zip uNames uLocation, Map.fromList $! zip uNames uTypes) b2w :: Bool -> GLuint b2w True = 1 b2w False = 0 mkUniformSetter :: InputType -> IO (GLUniform, InputSetter) mkUniformSetter t@Bool = do {r <- newIORef 0; return $! (GLUniform t r, SBool $! writeIORef r . b2w)} mkUniformSetter t@V2B = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2B $! writeIORef r . fmap b2w)} mkUniformSetter t@V3B = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3B $! writeIORef r . fmap b2w)} mkUniformSetter t@V4B = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4B $! writeIORef r . fmap b2w)} mkUniformSetter t@Word = do {r <- newIORef 0; return $! (GLUniform t r, SWord $! writeIORef r)} mkUniformSetter t@V2U = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2U $! writeIORef r)} mkUniformSetter t@V3U = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3U $! writeIORef r)} mkUniformSetter t@V4U = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4U $! writeIORef r)} mkUniformSetter t@Int = do {r <- newIORef 0; return $! (GLUniform t r, SInt $! writeIORef r)} mkUniformSetter t@V2I = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2I $! writeIORef r)} mkUniformSetter t@V3I = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3I $! writeIORef r)} mkUniformSetter t@V4I = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4I $! writeIORef r)} mkUniformSetter t@Float = do {r <- newIORef 0; return $! (GLUniform t r, SFloat $! writeIORef r)} mkUniformSetter t@V2F = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2F $! writeIORef r)} mkUniformSetter t@V3F = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3F $! writeIORef r)} mkUniformSetter t@V4F = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4F $! writeIORef r)} mkUniformSetter t@M22F = do {r <- newIORef (V2 z2 z2); return $! (GLUniform t r, SM22F $! writeIORef r)} mkUniformSetter t@M23F = do {r <- newIORef (V3 z2 z2 z2); return $! (GLUniform t r, SM23F $! writeIORef r)} mkUniformSetter t@M24F = do {r <- newIORef (V4 z2 z2 z2 z2); return $! (GLUniform t r, SM24F $! writeIORef r)} mkUniformSetter t@M32F = do {r <- newIORef (V2 z3 z3); return $! (GLUniform t r, SM32F $! writeIORef r)} mkUniformSetter t@M33F = do {r <- newIORef (V3 z3 z3 z3); return $! (GLUniform t r, SM33F $! writeIORef r)} mkUniformSetter t@M34F = do {r <- newIORef (V4 z3 z3 z3 z3); return $! (GLUniform t r, SM34F $! writeIORef r)} mkUniformSetter t@M42F = do {r <- newIORef (V2 z4 z4); return $! (GLUniform t r, SM42F $! writeIORef r)} mkUniformSetter t@M43F = do {r <- newIORef (V3 z4 z4 z4); return $! (GLUniform t r, SM43F $! writeIORef r)} mkUniformSetter t@M44F = do {r <- newIORef (V4 z4 z4 z4 z4); return $! (GLUniform t r, SM44F $! writeIORef r)} mkUniformSetter t@FTexture2D = do {r <- newIORef (TextureData 0); return $! (GLUniform t r, SFTexture2D $! writeIORef r)} -- sets value based uniforms only (does not handle textures) setUniform :: Storable a => GLint -> InputType -> IORef a -> IO () setUniform i ty ref = do v <- readIORef ref let false = fromIntegral GL_FALSE with v $ \p -> case ty of Bool -> glUniform1uiv i 1 (castPtr p) V2B -> glUniform2uiv i 1 (castPtr p) V3B -> glUniform3uiv i 1 (castPtr p) V4B -> glUniform4uiv i 1 (castPtr p) Word -> glUniform1uiv i 1 (castPtr p) V2U -> glUniform2uiv i 1 (castPtr p) V3U -> glUniform3uiv i 1 (castPtr p) V4U -> glUniform4uiv i 1 (castPtr p) Int -> glUniform1iv i 1 (castPtr p) V2I -> glUniform2iv i 1 (castPtr p) V3I -> glUniform3iv i 1 (castPtr p) V4I -> glUniform4iv i 1 (castPtr p) Float -> glUniform1fv i 1 (castPtr p) V2F -> glUniform2fv i 1 (castPtr p) V3F -> glUniform3fv i 1 (castPtr p) V4F -> glUniform4fv i 1 (castPtr p) M22F -> glUniformMatrix2fv i 1 false (castPtr p) M23F -> glUniformMatrix2x3fv i 1 false (castPtr p) M24F -> glUniformMatrix2x4fv i 1 false (castPtr p) M32F -> glUniformMatrix3x2fv i 1 false (castPtr p) M33F -> glUniformMatrix3fv i 1 false (castPtr p) M34F -> glUniformMatrix3x4fv i 1 false (castPtr p) M42F -> glUniformMatrix4x2fv i 1 false (castPtr p) M43F -> glUniformMatrix4x3fv i 1 false (castPtr p) M44F -> glUniformMatrix4fv i 1 false (castPtr p) FTexture2D -> return () --putStrLn $ "TODO: setUniform FTexture2D" _ -> fail $ "internal error (setUniform)! - " ++ show ty -- attribute functions queryStreams :: GLuint -> IO (Map String GLuint, Map String InputType) queryStreams po = do al <- getNameTypeSize po glGetActiveAttrib glGetAttribLocation GL_ACTIVE_ATTRIBUTES GL_ACTIVE_ATTRIBUTE_MAX_LENGTH let aNames = [n | (n,_,_,_) <- al] aTypes = [fromGLType (e,s) | (_,_,e,s) <- al] aLocation = [fromIntegral i | (_,i,_,_) <- al] return $! (Map.fromList $! zip aNames aLocation, Map.fromList $! zip aNames aTypes) arrayTypeToGLType :: ArrayType -> GLenum arrayTypeToGLType a = case a of ArrWord8 -> GL_UNSIGNED_BYTE ArrWord16 -> GL_UNSIGNED_SHORT ArrWord32 -> GL_UNSIGNED_INT ArrInt8 -> GL_BYTE ArrInt16 -> GL_SHORT ArrInt32 -> GL_INT ArrFloat -> GL_FLOAT ArrHalf -> GL_HALF_FLOAT setVertexAttrib :: GLuint -> Stream Buffer -> IO () setVertexAttrib i val = case val of ConstWord v -> with v $! \p -> glVertexAttribI1uiv i $! castPtr p ConstV2U v -> with v $! \p -> glVertexAttribI2uiv i $! castPtr p ConstV3U v -> with v $! \p -> glVertexAttribI3uiv i $! castPtr p ConstV4U v -> with v $! \p -> glVertexAttribI4uiv i $! castPtr p ConstInt v -> with v $! \p -> glVertexAttribI1iv i $! castPtr p ConstV2I v -> with v $! \p -> glVertexAttribI2iv i $! castPtr p ConstV3I v -> with v $! \p -> glVertexAttribI3iv i $! castPtr p ConstV4I v -> with v $! \p -> glVertexAttribI4iv i $! castPtr p ConstFloat v -> setAFloat i v ConstV2F v -> setAV2F i v ConstV3F v -> setAV3F i v ConstV4F v -> setAV4F i v ConstM22F (V2 x y) -> setAV2F i x >> setAV2F (i+1) y ConstM23F (V3 x y z) -> setAV2F i x >> setAV2F (i+1) y >> setAV2F (i+2) z ConstM24F (V4 x y z w) -> setAV2F i x >> setAV2F (i+1) y >> setAV2F (i+2) z >> setAV2F (i+3) w ConstM32F (V2 x y) -> setAV3F i x >> setAV3F (i+1) y ConstM33F (V3 x y z) -> setAV3F i x >> setAV3F (i+1) y >> setAV3F (i+2) z ConstM34F (V4 x y z w) -> setAV3F i x >> setAV3F (i+1) y >> setAV3F (i+2) z >> setAV3F (i+3) w ConstM42F (V2 x y) -> setAV4F i x >> setAV4F (i+1) y ConstM43F (V3 x y z) -> setAV4F i x >> setAV4F (i+1) y >> setAV4F (i+2) z ConstM44F (V4 x y z w) -> setAV4F i x >> setAV4F (i+1) y >> setAV4F (i+2) z >> setAV4F (i+3) w _ -> fail "internal error (setVertexAttrib)!" setAFloat :: GLuint -> Float -> IO () setAV2F :: GLuint -> V2F -> IO () setAV3F :: GLuint -> V3F -> IO () setAV4F :: GLuint -> V4F -> IO () setAFloat i v = with v $! \p -> glVertexAttrib1fv i $! castPtr p setAV2F i v = with v $! \p -> glVertexAttrib2fv i $! castPtr p setAV3F i v = with v $! \p -> glVertexAttrib3fv i $! castPtr p setAV4F i v = with v $! \p -> glVertexAttrib4fv i $! castPtr p -- result list: [(name string,location,gl type,component count)] getNameTypeSize :: GLuint -> (GLuint -> GLuint -> GLsizei -> Ptr GLsizei -> Ptr GLint -> Ptr GLenum -> Ptr GLchar -> IO ()) -> (GLuint -> Ptr GLchar -> IO GLint) -> GLenum -> GLenum -> IO [(String,GLint,GLenum,GLint)] getNameTypeSize o f g enum enumLen = do nameLen <- glGetProgramiv1 enumLen o allocaArray (fromIntegral nameLen) $! \namep -> alloca $! \sizep -> alloca $! \typep -> do n <- glGetProgramiv1 enum o forM [0..n-1] $! \i -> f o (fromIntegral i) (fromIntegral nameLen) nullPtr sizep typep namep >> (,,,) <$> peekCString (castPtr namep) <*> g o namep <*> peek typep <*> peek sizep fromGLType :: (GLenum,GLint) -> InputType fromGLType (t,1) | t == GL_BOOL = Bool | t == GL_BOOL_VEC2 = V2B | t == GL_BOOL_VEC3 = V3B | t == GL_BOOL_VEC4 = V4B | t == GL_UNSIGNED_INT = Word | t == GL_UNSIGNED_INT_VEC2 = V2U | t == GL_UNSIGNED_INT_VEC3 = V3U | t == GL_UNSIGNED_INT_VEC4 = V4U | t == GL_INT = Int | t == GL_INT_VEC2 = V2I | t == GL_INT_VEC3 = V3I | t == GL_INT_VEC4 = V4I | t == GL_FLOAT = Float | t == GL_FLOAT_VEC2 = V2F | t == GL_FLOAT_VEC3 = V3F | t == GL_FLOAT_VEC4 = V4F | t == GL_FLOAT_MAT2 = M22F | t == GL_FLOAT_MAT2x3 = M23F | t == GL_FLOAT_MAT2x4 = M24F | t == GL_FLOAT_MAT3x2 = M32F | t == GL_FLOAT_MAT3 = M33F | t == GL_FLOAT_MAT3x4 = M34F | t == GL_FLOAT_MAT4x2 = M42F | t == GL_FLOAT_MAT4x3 = M43F | t == GL_FLOAT_MAT4 = M44F | t == GL_SAMPLER_1D_ARRAY_SHADOW = STexture1DArray | t == GL_SAMPLER_1D_SHADOW = STexture1D | t == GL_SAMPLER_2D_ARRAY_SHADOW = STexture2DArray | t == GL_SAMPLER_2D_RECT_SHADOW = STexture2DRect | t == GL_SAMPLER_2D_SHADOW = STexture2D | t == GL_SAMPLER_CUBE_SHADOW = STextureCube | t == GL_INT_SAMPLER_1D = ITexture1D | t == GL_INT_SAMPLER_1D_ARRAY = ITexture1DArray | t == GL_INT_SAMPLER_2D = ITexture2D | t == GL_INT_SAMPLER_2D_ARRAY = ITexture2DArray | t == GL_INT_SAMPLER_2D_MULTISAMPLE = ITexture2DMS | t == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = ITexture2DMSArray | t == GL_INT_SAMPLER_2D_RECT = ITexture2DRect | t == GL_INT_SAMPLER_3D = ITexture3D | t == GL_INT_SAMPLER_BUFFER = ITextureBuffer | t == GL_INT_SAMPLER_CUBE = ITextureCube | t == GL_SAMPLER_1D = FTexture1D | t == GL_SAMPLER_1D_ARRAY = FTexture1DArray | t == GL_SAMPLER_2D = FTexture2D | t == GL_SAMPLER_2D_ARRAY = FTexture2DArray | t == GL_SAMPLER_2D_MULTISAMPLE = FTexture2DMS | t == GL_SAMPLER_2D_MULTISAMPLE_ARRAY = FTexture2DMSArray | t == GL_SAMPLER_2D_RECT = FTexture2DRect | t == GL_SAMPLER_3D = FTexture3D | t == GL_SAMPLER_BUFFER = FTextureBuffer | t == GL_SAMPLER_CUBE = FTextureCube | t == GL_UNSIGNED_INT_SAMPLER_1D = UTexture1D | t == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY = UTexture1DArray | t == GL_UNSIGNED_INT_SAMPLER_2D = UTexture2D | t == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY = UTexture2DArray | t == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = UTexture2DMS | t == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = UTexture2DMSArray | t == GL_UNSIGNED_INT_SAMPLER_2D_RECT = UTexture2DRect | t == GL_UNSIGNED_INT_SAMPLER_3D = UTexture3D | t == GL_UNSIGNED_INT_SAMPLER_BUFFER = UTextureBuffer | t == GL_UNSIGNED_INT_SAMPLER_CUBE = UTextureCube | otherwise = error "Failed fromGLType" fromGLUniformType _ = error "Failed fromGLType" printShaderLog :: GLuint -> IO String printShaderLog o = do i <- glGetShaderiv1 GL_INFO_LOG_LENGTH o case (i > 0) of False -> return "" True -> do alloca $ \sizePtr -> allocaArray (fromIntegral i) $! \ps -> do glGetShaderInfoLog o (fromIntegral i) sizePtr ps size <- peek sizePtr log <- peekCStringLen (castPtr ps, fromIntegral size) putStrLn log return log glGetShaderiv1 :: GLenum -> GLuint -> IO GLint glGetShaderiv1 pname o = alloca $! \pi -> glGetShaderiv o pname pi >> peek pi glGetProgramiv1 :: GLenum -> GLuint -> IO GLint glGetProgramiv1 pname o = alloca $! \pi -> glGetProgramiv o pname pi >> peek pi printProgramLog :: GLuint -> IO String printProgramLog o = do i <- glGetProgramiv1 GL_INFO_LOG_LENGTH o case (i > 0) of False -> return "" True -> do alloca $ \sizePtr -> allocaArray (fromIntegral i) $! \ps -> do glGetProgramInfoLog o (fromIntegral i) sizePtr ps size <- peek sizePtr log <- peekCStringLen (castPtr ps, fromIntegral size) unless (null log) $ putStrLn log return log compileShader :: GLuint -> [String] -> IO () compileShader o srcl = withMany withCString srcl $! \l -> withArray l $! \p -> do glShaderSource o (fromIntegral $! length srcl) (castPtr p) nullPtr glCompileShader o log <- printShaderLog o status <- glGetShaderiv1 GL_COMPILE_STATUS o when (status /= fromIntegral GL_TRUE) $ fail $ unlines ["compileShader failed:",log] checkGL :: IO String checkGL = do let f e | e == GL_INVALID_ENUM = "INVALID_ENUM" | e == GL_INVALID_VALUE = "INVALID_VALUE" | e == GL_INVALID_OPERATION = "INVALID_OPERATION" | e == GL_INVALID_FRAMEBUFFER_OPERATION = "INVALID_FRAMEBUFFER_OPERATION" | e == GL_OUT_OF_MEMORY = "OUT_OF_MEMORY" | e == GL_NO_ERROR = "OK" | otherwise = "Unknown error" e <- glGetError return $ f e streamToInputType :: Stream Buffer -> InputType streamToInputType s = case s of ConstWord _ -> Word ConstV2U _ -> V2U ConstV3U _ -> V3U ConstV4U _ -> V4U ConstInt _ -> Int ConstV2I _ -> V2I ConstV3I _ -> V3I ConstV4I _ -> V4I ConstFloat _ -> Float ConstV2F _ -> V2F ConstV3F _ -> V3F ConstV4F _ -> V4F ConstM22F _ -> M22F ConstM23F _ -> M23F ConstM24F _ -> M24F ConstM32F _ -> M32F ConstM33F _ -> M33F ConstM34F _ -> M34F ConstM42F _ -> M42F ConstM43F _ -> M43F ConstM44F _ -> M44F Stream t (Buffer a _) i _ _ | 0 <= i && i < V.length a && if elem t integralTypes then elem at integralArrTypes else True -> fromStreamType t | otherwise -> error "streamToInputType failed" where at = arrType $! (a V.! i) integralTypes = [Attribute_Word, Attribute_V2U, Attribute_V3U, Attribute_V4U, Attribute_Int, Attribute_V2I, Attribute_V3I, Attribute_V4I] integralArrTypes = [ArrWord8, ArrWord16, ArrWord32, ArrInt8, ArrInt16, ArrInt32] comparisonFunctionToGLType :: ComparisonFunction -> GLenum comparisonFunctionToGLType a = case a of Always -> GL_ALWAYS Equal -> GL_EQUAL Gequal -> GL_GEQUAL Greater -> GL_GREATER Lequal -> GL_LEQUAL Less -> GL_LESS Never -> GL_NEVER Notequal -> GL_NOTEQUAL logicOperationToGLType :: LogicOperation -> GLenum logicOperationToGLType a = case a of And -> GL_AND AndInverted -> GL_AND_INVERTED AndReverse -> GL_AND_REVERSE Clear -> GL_CLEAR Copy -> GL_COPY CopyInverted -> GL_COPY_INVERTED Equiv -> GL_EQUIV Invert -> GL_INVERT Nand -> GL_NAND Noop -> GL_NOOP Nor -> GL_NOR Or -> GL_OR OrInverted -> GL_OR_INVERTED OrReverse -> GL_OR_REVERSE Set -> GL_SET Xor -> GL_XOR blendEquationToGLType :: BlendEquation -> GLenum blendEquationToGLType a = case a of FuncAdd -> GL_FUNC_ADD FuncReverseSubtract -> GL_FUNC_REVERSE_SUBTRACT FuncSubtract -> GL_FUNC_SUBTRACT Max -> GL_MAX Min -> GL_MIN blendingFactorToGLType :: BlendingFactor -> GLenum blendingFactorToGLType a = case a of ConstantAlpha -> GL_CONSTANT_ALPHA ConstantColor -> GL_CONSTANT_COLOR DstAlpha -> GL_DST_ALPHA DstColor -> GL_DST_COLOR One -> GL_ONE OneMinusConstantAlpha -> GL_ONE_MINUS_CONSTANT_ALPHA OneMinusConstantColor -> GL_ONE_MINUS_CONSTANT_COLOR OneMinusDstAlpha -> GL_ONE_MINUS_DST_ALPHA OneMinusDstColor -> GL_ONE_MINUS_DST_COLOR OneMinusSrcAlpha -> GL_ONE_MINUS_SRC_ALPHA OneMinusSrcColor -> GL_ONE_MINUS_SRC_COLOR SrcAlpha -> GL_SRC_ALPHA SrcAlphaSaturate -> GL_SRC_ALPHA_SATURATE SrcColor -> GL_SRC_COLOR Zero -> GL_ZERO textureDataTypeToGLType :: ImageSemantic -> TextureDataType -> GLenum textureDataTypeToGLType Color a = case a of FloatT Red -> GL_R32F IntT Red -> GL_R32I WordT Red -> GL_R32UI FloatT RG -> GL_RG32F IntT RG -> GL_RG32I WordT RG -> GL_RG32UI FloatT RGBA -> GL_RGBA32F IntT RGBA -> GL_RGBA32I WordT RGBA -> GL_RGBA32UI a -> error $ "FIXME: This texture format is not yet supported" ++ show a textureDataTypeToGLType Depth a = case a of FloatT Red -> GL_DEPTH_COMPONENT32F WordT Red -> GL_DEPTH_COMPONENT32 a -> error $ "FIXME: This texture format is not yet supported" ++ show a textureDataTypeToGLType Stencil a = case a of a -> error $ "FIXME: This texture format is not yet supported" ++ show a textureDataTypeToGLArityType :: ImageSemantic -> TextureDataType -> GLenum textureDataTypeToGLArityType Color a = case a of FloatT Red -> GL_RED IntT Red -> GL_RED WordT Red -> GL_RED FloatT RG -> GL_RG IntT RG -> GL_RG WordT RG -> GL_RG FloatT RGBA -> GL_RGBA IntT RGBA -> GL_RGBA WordT RGBA -> GL_RGBA a -> error $ "FIXME: This texture format is not yet supported" ++ show a textureDataTypeToGLArityType Depth a = case a of FloatT Red -> GL_DEPTH_COMPONENT WordT Red -> GL_DEPTH_COMPONENT a -> error $ "FIXME: This texture format is not yet supported" ++ show a textureDataTypeToGLArityType Stencil a = case a of a -> error $ "FIXME: This texture format is not yet supported" ++ show a {- Texture and renderbuffer color formats (R): R11F_G11F_B10F R16 R16F R16I R16UI R32F R32I R32UI R8 R8I R8UI RG16 RG16F RG16I RG16UI RG32F RG32I RG32UI RG8 RG8I RG8UI RGB10_A2 RGB10_A2UI RGBA16 RGBA16F RGBA16I RGBA16UI RGBA32F RGBA32I RGBA32UI RGBA8 RGBA8I RGBA8UI SRGB8_ALPHA8 -} glGetIntegerv1 :: GLenum -> IO GLint glGetIntegerv1 e = alloca $ \pi -> glGetIntegerv e pi >> peek pi checkFBO :: IO String checkFBO = do let f e | e == GL_FRAMEBUFFER_UNDEFINED = "FRAMEBUFFER_UNDEFINED" | e == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = "FRAMEBUFFER_INCOMPLETE_ATTACHMENT" | e == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" | e == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER = "FRAMEBUFFER_INCOMPLETE_READ_BUFFER" | e == GL_FRAMEBUFFER_UNSUPPORTED = "FRAMEBUFFER_UNSUPPORTED" | e == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" | e == GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS = "FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS" | e == GL_FRAMEBUFFER_COMPLETE = "FRAMEBUFFER_COMPLETE" | otherwise = "Unknown error" e <- glCheckFramebufferStatus GL_DRAW_FRAMEBUFFER return $ f e filterToGLType :: Filter -> GLenum filterToGLType a = case a of Nearest -> GL_NEAREST Linear -> GL_LINEAR NearestMipmapNearest -> GL_NEAREST_MIPMAP_NEAREST NearestMipmapLinear -> GL_NEAREST_MIPMAP_LINEAR LinearMipmapNearest -> GL_LINEAR_MIPMAP_NEAREST LinearMipmapLinear -> GL_LINEAR_MIPMAP_LINEAR edgeModeToGLType :: EdgeMode -> GLenum edgeModeToGLType a = case a of Repeat -> GL_REPEAT MirroredRepeat -> GL_MIRRORED_REPEAT ClampToEdge -> GL_CLAMP_TO_EDGE ClampToBorder -> GL_CLAMP_TO_BORDER data ParameterSetup = ParameterSetup { setParameteri :: GLenum -> GLint -> IO () , setParameterfv :: GLenum -> Ptr GLfloat -> IO () , setParameterIiv :: GLenum -> Ptr GLint -> IO () , setParameterIuiv :: GLenum -> Ptr GLuint -> IO () , setParameterf :: GLenum -> GLfloat -> IO () } setTextureSamplerParameters :: GLenum -> SamplerDescriptor -> IO () setTextureSamplerParameters target = setParameters $ ParameterSetup { setParameteri = glTexParameteri target , setParameterfv = glTexParameterfv target , setParameterIiv = glTexParameterIiv target , setParameterIuiv = glTexParameterIuiv target , setParameterf = glTexParameterf target } setSamplerParameters :: GLuint -> SamplerDescriptor -> IO () setSamplerParameters samplerObj = setParameters $ ParameterSetup { setParameteri = glSamplerParameteri samplerObj , setParameterfv = glSamplerParameterfv samplerObj , setParameterIiv = glSamplerParameterIiv samplerObj , setParameterIuiv = glSamplerParameterIuiv samplerObj , setParameterf = glSamplerParameterf samplerObj } setParameters :: ParameterSetup -> SamplerDescriptor -> IO () setParameters ParameterSetup{..} s = do setParameteri GL_TEXTURE_WRAP_S $ fromIntegral $ edgeModeToGLType $ samplerWrapS s case samplerWrapT s of Nothing -> return () Just a -> setParameteri GL_TEXTURE_WRAP_T $ fromIntegral $ edgeModeToGLType a case samplerWrapR s of Nothing -> return () Just a -> setParameteri GL_TEXTURE_WRAP_R $ fromIntegral $ edgeModeToGLType a setParameteri GL_TEXTURE_MIN_FILTER $ fromIntegral $ filterToGLType $ samplerMinFilter s setParameteri GL_TEXTURE_MAG_FILTER $ fromIntegral $ filterToGLType $ samplerMagFilter s let setBColorV4F a = with a $ \p -> setParameterfv GL_TEXTURE_BORDER_COLOR $ castPtr p setBColorV4I a = with a $ \p -> setParameterIiv GL_TEXTURE_BORDER_COLOR $ castPtr p setBColorV4U a = with a $ \p -> setParameterIuiv GL_TEXTURE_BORDER_COLOR $ castPtr p case samplerBorderColor s of -- float, word, int, red, rg, rgb, rgba VFloat a -> setBColorV4F $ V4 a 0 0 0 VV2F (V2 a b) -> setBColorV4F $ V4 a b 0 0 VV3F (V3 a b c) -> setBColorV4F $ V4 a b c 0 VV4F a -> setBColorV4F a VInt a -> setBColorV4I $ V4 a 0 0 0 VV2I (V2 a b) -> setBColorV4I $ V4 a b 0 0 VV3I (V3 a b c) -> setBColorV4I $ V4 a b c 0 VV4I a -> setBColorV4I a VWord a -> setBColorV4U $ V4 a 0 0 0 VV2U (V2 a b) -> setBColorV4U $ V4 a b 0 0 VV3U (V3 a b c) -> setBColorV4U $ V4 a b c 0 VV4U a -> setBColorV4U a _ -> fail "internal error (setTextureSamplerParameters)!" case samplerMinLod s of Nothing -> return () Just a -> setParameterf GL_TEXTURE_MIN_LOD $ realToFrac a case samplerMaxLod s of Nothing -> return () Just a -> setParameterf GL_TEXTURE_MAX_LOD $ realToFrac a setParameterf GL_TEXTURE_LOD_BIAS $ realToFrac $ samplerLodBias s case samplerCompareFunc s of Nothing -> setParameteri GL_TEXTURE_COMPARE_MODE $ fromIntegral GL_NONE Just a -> do setParameteri GL_TEXTURE_COMPARE_MODE $ fromIntegral GL_COMPARE_REF_TO_TEXTURE setParameteri GL_TEXTURE_COMPARE_FUNC $ fromIntegral $ comparisonFunctionToGLType a compileSampler :: SamplerDescriptor -> IO GLSampler compileSampler s = do so <- alloca $! \po -> glGenSamplers 1 po >> peek po setSamplerParameters so s return $ GLSampler { glSamplerObject = so } compileTexture :: TextureDescriptor -> IO GLTexture compileTexture txDescriptor = do to <- alloca $! \pto -> glGenTextures 1 pto >> peek pto let TextureDescriptor { textureType = txType , textureSize = txSize , textureSemantic = txSemantic , textureSampler = txSampler , textureBaseLevel = txBaseLevel , textureMaxLevel = txMaxLevel } = txDescriptor txSetup txTarget dTy = do let internalFormat = fromIntegral $ textureDataTypeToGLType txSemantic dTy dataFormat = fromIntegral $ textureDataTypeToGLArityType txSemantic dTy glBindTexture txTarget to glTexParameteri txTarget GL_TEXTURE_BASE_LEVEL $ fromIntegral txBaseLevel glTexParameteri txTarget GL_TEXTURE_MAX_LEVEL $ fromIntegral txMaxLevel setTextureSamplerParameters txTarget txSampler return (internalFormat,dataFormat) mipSize 0 x = [x] mipSize n x = x : mipSize (n-1) (x `div` 2) mipS = mipSize (txMaxLevel - txBaseLevel) levels = [txBaseLevel..txMaxLevel] target <- case txType of Texture1D dTy layerCnt -> do let VWord txW = txSize txTarget = if layerCnt > 1 then GL_TEXTURE_1D_ARRAY else GL_TEXTURE_1D (internalFormat,dataFormat) <- txSetup txTarget dTy forM_ (zip levels (mipS txW)) $ \(l,w) -> case layerCnt > 1 of True -> glTexImage2D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral layerCnt) 0 dataFormat GL_UNSIGNED_BYTE nullPtr False -> glTexImage1D txTarget (fromIntegral l) internalFormat (fromIntegral w) 0 dataFormat GL_UNSIGNED_BYTE nullPtr return txTarget Texture2D dTy layerCnt -> do let VV2U (V2 txW txH) = txSize txTarget = if layerCnt > 1 then GL_TEXTURE_2D_ARRAY else GL_TEXTURE_2D (internalFormat,dataFormat) <- txSetup txTarget dTy forM_ (zip3 levels (mipS txW) (mipS txH)) $ \(l,w,h) -> case layerCnt > 1 of True -> glTexImage3D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) (fromIntegral layerCnt) 0 dataFormat GL_UNSIGNED_BYTE nullPtr False -> glTexImage2D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat GL_UNSIGNED_BYTE nullPtr return txTarget Texture3D dTy -> do let VV3U (V3 txW txH txD) = txSize txTarget = GL_TEXTURE_3D (internalFormat,dataFormat) <- txSetup txTarget dTy forM_ (zip4 levels (mipS txW) (mipS txH) (mipS txD)) $ \(l,w,h,d) -> glTexImage3D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) (fromIntegral d) 0 dataFormat GL_UNSIGNED_BYTE nullPtr return txTarget TextureCube dTy -> do let VV2U (V2 txW txH) = txSize txTarget = GL_TEXTURE_CUBE_MAP targets = [ GL_TEXTURE_CUBE_MAP_POSITIVE_X , GL_TEXTURE_CUBE_MAP_NEGATIVE_X , GL_TEXTURE_CUBE_MAP_POSITIVE_Y , GL_TEXTURE_CUBE_MAP_NEGATIVE_Y , GL_TEXTURE_CUBE_MAP_POSITIVE_Z , GL_TEXTURE_CUBE_MAP_NEGATIVE_Z ] (internalFormat,dataFormat) <- txSetup txTarget dTy forM_ (zip3 levels (mipS txW) (mipS txH)) $ \(l,w,h) -> forM_ targets $ \t -> glTexImage2D t (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat GL_UNSIGNED_BYTE nullPtr return txTarget TextureRect dTy -> do let VV2U (V2 txW txH) = txSize txTarget = GL_TEXTURE_RECTANGLE (internalFormat,dataFormat) <- txSetup txTarget dTy forM_ (zip3 levels (mipS txW) (mipS txH)) $ \(l,w,h) -> glTexImage2D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat GL_UNSIGNED_BYTE nullPtr return txTarget Texture2DMS dTy layerCnt sampleCount isFixedLocations -> do let VV2U (V2 w h) = txSize txTarget = if layerCnt > 1 then GL_TEXTURE_2D_MULTISAMPLE_ARRAY else GL_TEXTURE_2D_MULTISAMPLE isFixed = fromIntegral $ if isFixedLocations then GL_TRUE else GL_FALSE (internalFormat,dataFormat) <- txSetup txTarget dTy case layerCnt > 1 of True -> glTexImage3DMultisample txTarget (fromIntegral sampleCount) internalFormat (fromIntegral w) (fromIntegral h) (fromIntegral layerCnt) isFixed False -> glTexImage2DMultisample txTarget (fromIntegral sampleCount) internalFormat (fromIntegral w) (fromIntegral h) isFixed return txTarget TextureBuffer dTy -> do fail "internal error: buffer texture is not supported yet" -- TODO let VV2U (V2 w h) = txSize txTarget = GL_TEXTURE_2D (internalFormat,dataFormat) <- txSetup txTarget dTy glTexImage2D GL_TEXTURE_2D 0 internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat GL_UNSIGNED_BYTE nullPtr return txTarget return $ GLTexture { glTextureObject = to , glTextureTarget = target } primitiveToFetchPrimitive :: Primitive -> FetchPrimitive primitiveToFetchPrimitive prim = case prim of TriangleStrip -> Triangles TriangleList -> Triangles TriangleFan -> Triangles LineStrip -> Lines LineList -> Lines PointList -> Points TriangleStripAdjacency -> TrianglesAdjacency TriangleListAdjacency -> TrianglesAdjacency LineStripAdjacency -> LinesAdjacency LineListAdjacency -> LinesAdjacency primitiveToGLType :: Primitive -> GLenum primitiveToGLType p = case p of TriangleStrip -> GL_TRIANGLE_STRIP TriangleList -> GL_TRIANGLES TriangleFan -> GL_TRIANGLE_FAN LineStrip -> GL_LINE_STRIP LineList -> GL_LINES PointList -> GL_POINTS TriangleStripAdjacency -> GL_TRIANGLE_STRIP_ADJACENCY TriangleListAdjacency -> GL_TRIANGLES_ADJACENCY LineStripAdjacency -> GL_LINE_STRIP_ADJACENCY LineListAdjacency -> GL_LINES_ADJACENCY inputTypeToTextureTarget :: InputType -> GLenum inputTypeToTextureTarget ty = case ty of STexture1D -> GL_TEXTURE_1D STexture2D -> GL_TEXTURE_2D STextureCube -> GL_TEXTURE_CUBE_MAP STexture1DArray -> GL_TEXTURE_1D_ARRAY STexture2DArray -> GL_TEXTURE_2D_ARRAY STexture2DRect -> GL_TEXTURE_RECTANGLE FTexture1D -> GL_TEXTURE_1D FTexture2D -> GL_TEXTURE_2D FTexture3D -> GL_TEXTURE_3D FTextureCube -> GL_TEXTURE_CUBE_MAP FTexture1DArray -> GL_TEXTURE_1D_ARRAY FTexture2DArray -> GL_TEXTURE_2D_ARRAY FTexture2DMS -> GL_TEXTURE_2D_MULTISAMPLE FTexture2DMSArray -> GL_TEXTURE_2D_MULTISAMPLE_ARRAY FTextureBuffer -> GL_TEXTURE_BUFFER FTexture2DRect -> GL_TEXTURE_RECTANGLE ITexture1D -> GL_TEXTURE_1D ITexture2D -> GL_TEXTURE_2D ITexture3D -> GL_TEXTURE_3D ITextureCube -> GL_TEXTURE_CUBE_MAP ITexture1DArray -> GL_TEXTURE_1D_ARRAY ITexture2DArray -> GL_TEXTURE_2D_ARRAY ITexture2DMS -> GL_TEXTURE_2D_MULTISAMPLE ITexture2DMSArray -> GL_TEXTURE_2D_MULTISAMPLE_ARRAY ITextureBuffer -> GL_TEXTURE_BUFFER ITexture2DRect -> GL_TEXTURE_RECTANGLE UTexture1D -> GL_TEXTURE_1D UTexture2D -> GL_TEXTURE_2D UTexture3D -> GL_TEXTURE_3D UTextureCube -> GL_TEXTURE_CUBE_MAP UTexture1DArray -> GL_TEXTURE_1D_ARRAY UTexture2DArray -> GL_TEXTURE_2D_ARRAY UTexture2DMS -> GL_TEXTURE_2D_MULTISAMPLE UTexture2DMSArray -> GL_TEXTURE_2D_MULTISAMPLE_ARRAY UTextureBuffer -> GL_TEXTURE_BUFFER UTexture2DRect -> GL_TEXTURE_RECTANGLE _ -> error "internal error (inputTypeToTextureTarget)!"