module Graphics.Luminance.Core.Texture where
import Control.Monad ( when )
import Control.Monad.IO.Class ( MonadIO(..) )
import Control.Monad.Trans.Resource ( MonadResource, register )
import Data.Proxy ( Proxy(..) )
import Data.Vector.Storable ( Vector )
import Foreign.Marshal.Alloc ( alloca )
import Foreign.Marshal.Utils ( with )
import Foreign.Storable ( Storable(peek) )
import Graphics.GL
import Graphics.GL.Ext.ARB.BindlessTexture
import Numeric.Natural ( Natural )
data Wrap
= ClampToEdge
| Repeat
| MirroredRepeat
deriving (Eq,Show)
fromWrap :: (Eq a,Num a) => Wrap -> a
fromWrap w = case w of
ClampToEdge -> GL_CLAMP_TO_EDGE
Repeat -> GL_REPEAT
MirroredRepeat -> GL_MIRRORED_REPEAT
data Filter
= Nearest
| Linear
deriving (Eq,Show)
fromFilter :: (Eq a,Num a) => Filter -> a
fromFilter f = case f of
Nearest -> GL_NEAREST
Linear -> GL_LINEAR
data CompareFunc
= Never
| Less
| Equal
| LessOrEqual
| Greater
| GreaterOrEqual
| NotEqual
| Always
deriving (Eq,Show)
fromCompareFunc :: (Eq a,Num a) => CompareFunc -> a
fromCompareFunc f = case f of
Never -> GL_NEVER
Less -> GL_LESS
Equal -> GL_EQUAL
LessOrEqual -> GL_LEQUAL
Greater -> GL_GREATER
GreaterOrEqual -> GL_GEQUAL
NotEqual -> GL_NOTEQUAL
Always -> GL_ALWAYS
class Texture t where
type TextureSize t :: *
type TextureOffset t :: *
fromBaseTexture :: BaseTexture -> TextureSize t -> t
toBaseTexture :: t -> BaseTexture
textureTypeEnum :: proxy t -> GLenum
textureSize :: t -> TextureSize t
textureStorage :: proxy t
-> GLuint
-> GLint
-> TextureSize t
-> IO ()
transferTexelsSub :: (Storable a)
=> proxy t
-> GLuint
-> TextureOffset t
-> TextureSize t
-> Vector a
-> IO ()
fillTextureSub :: (Storable a)
=> proxy t
-> GLuint
-> TextureOffset t
-> TextureSize t
-> Vector a
-> IO ()
data BaseTexture = BaseTexture {
baseTextureID :: GLuint
, baseTextureHnd :: GLuint64
} deriving (Eq,Show)
createTexture :: forall m t. (MonadIO m,MonadResource m,Texture t)
=> TextureSize t
-> Natural
-> Sampling
-> m t
createTexture size levels sampling = do
(tid,texH) <- liftIO . alloca $ \p -> do
glCreateTextures (textureTypeEnum (Proxy :: Proxy t)) 1 p
tid <- peek p
textureStorage (Proxy :: Proxy t) tid (fromIntegral levels) size
glTextureParameteri tid GL_TEXTURE_BASE_LEVEL 0
glTextureParameteri tid GL_TEXTURE_MAX_LEVEL (fromIntegral levels 1)
setTextureSampling tid sampling
texH <- glGetTextureHandleARB tid
glMakeTextureHandleResidentARB texH
pure (tid,texH)
_ <- register $ do
glMakeTextureHandleNonResidentARB texH
with tid $ glDeleteTextures 1
pure $ fromBaseTexture (BaseTexture tid texH) size
data Sampling = Sampling {
samplingWrapS :: Wrap
, samplingWrapT :: Wrap
, samplingWrapR :: Wrap
, samplingMinFilter :: Filter
, samplingMagFilter :: Filter
, samplingCompareFunction :: Maybe CompareFunc
} deriving (Eq,Show)
defaultSampling :: Sampling
defaultSampling = Sampling {
samplingWrapS = ClampToEdge
, samplingWrapT = ClampToEdge
, samplingWrapR = ClampToEdge
, samplingMinFilter = Linear
, samplingMagFilter = Linear
, samplingCompareFunction = Nothing
}
setSampling :: (Eq a,Eq b,MonadIO m,Num a,Num b) => (GLenum -> a -> b -> IO ()) -> GLenum -> Sampling -> m ()
setSampling f objID s = liftIO $ do
f objID GL_TEXTURE_WRAP_S . fromWrap $ samplingWrapS s
f objID GL_TEXTURE_WRAP_T . fromWrap $ samplingWrapT s
f objID GL_TEXTURE_WRAP_R . fromWrap $ samplingWrapR s
f objID GL_TEXTURE_MIN_FILTER . fromFilter $ samplingMinFilter s
f objID GL_TEXTURE_MAG_FILTER . fromFilter $ samplingMagFilter s
case samplingCompareFunction s of
Just cmpf -> do
f objID GL_TEXTURE_COMPARE_FUNC $ fromCompareFunc cmpf
f objID GL_TEXTURE_COMPARE_MODE GL_COMPARE_REF_TO_TEXTURE
Nothing ->
f objID GL_TEXTURE_COMPARE_MODE GL_NONE
setTextureSampling :: (MonadIO m) => GLenum -> Sampling -> m ()
setTextureSampling = setSampling glTextureParameteri
setSamplerSampling :: (MonadIO m) => GLenum -> Sampling -> m ()
setSamplerSampling = setSampling glSamplerParameteri
newtype Sampler = Sampler { samplerID :: GLuint } deriving (Eq,Show)
createSampler :: (MonadIO m,MonadResource m)
=> Sampling
-> m Sampler
createSampler s = do
sid <- liftIO . alloca $ \p -> do
glCreateSamplers 1 p
sid <- peek p
setSamplerSampling sid s
pure sid
_ <- register . with sid $ glDeleteSamplers 1
pure $ Sampler sid
uploadSub :: forall a m t. (MonadIO m,Storable a,Texture t)
=> t
-> TextureOffset t
-> TextureSize t
-> Bool
-> Vector a
-> m ()
uploadSub tex offset size autolvl texels = liftIO $ do
transferTexelsSub (Proxy :: Proxy t) tid offset size texels
when autolvl $ glGenerateTextureMipmap tid
where
tid = baseTextureID (toBaseTexture tex)
fillSub :: forall a m t. (MonadIO m,Storable a,Texture t)
=> t
-> TextureOffset t
-> TextureSize t
-> Bool
-> Vector a
-> m ()
fillSub tex offset size autolvl filling = liftIO $ do
fillTextureSub (Proxy :: Proxy t) tid offset size filling
when autolvl $ glGenerateTextureMipmap tid
where
tid = baseTextureID (toBaseTexture tex)