packages feed

h-raylib-5.1.1.0: src/Raylib/Internal.hs

{-# LANGUAGE CPP #-}
{-# OPTIONS -Wall #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Internal h-raylib utilities for automatic memory management
module Raylib.Internal
  ( WindowResources (..),
    defaultWindowResources,

    -- * Unloading individual resources
    unloadSingleShader,
    unloadSingleTexture,
    unloadSingleFrameBuffer,
    unloadSingleVaoId,
    unloadSingleVboIdList,
    unloadSingleCtxDataPtr,
    unloadSingleAudioBuffer,
    unloadSingleAudioBufferAlias,
    unloadSingleAutomationEventList,
    unloadSingleFunPtr,

    -- * Unloading all resources
    unloadShaders,
    unloadTextures,
    unloadFrameBuffers,
    unloadVaoIds,
    unloadVboIds,
    unloadCtxData,
    unloadAudioBuffers,
    unloadAudioBufferAliases,
    unloadAutomationEventLists,
    unloadFunPtrs,

    -- * Adding resources
    addShaderId,
    addTextureId,
    addFrameBuffer,
    addVaoId,
    addVboIds,
    addCtxData,
    addAudioBuffer,
    addAudioBufferAlias,
    addAutomationEventList,
    addFunPtr,

    -- * Miscellaneous
    c'rlGetShaderIdDefault,
    getPixelDataSize,
  )
where

import Control.Monad (forM_, unless, when)
import Data.IORef (IORef, modifyIORef, newIORef, readIORef)
import Data.List (delete)
import Data.Map (Map)
import qualified Data.Map as Map
import Foreign (FunPtr, Ptr, Storable (peekByteOff), free, freeHaskellFunPtr)
import Foreign.C (CInt (..), CUInt (..))
import GHC.IO (unsafePerformIO)
import Raylib.Internal.TH (genNative)

#ifdef WEB_FFI

import Raylib.Internal.Web.Native (callRaylibFunction)

#endif

-- | Tracks all raylib resources which cannot be immediately freed.
--
--   Each field is an `IORef` to a list, and the list contains the data to be
--   tracked. Typically, data allocated on the GPU is stored here.
data WindowResources = WindowResources
  { shaderIds :: IORef [CUInt],
    shaderLocations :: IORef (Map Integer (Map String Int)),
    textureIds :: IORef [CUInt],
    frameBuffers :: IORef [CUInt],
    vaoIds :: IORef [CUInt],
    vboIds :: IORef [CUInt],
    ctxDataPtrs :: IORef [(CInt, Ptr ())],
    audioBuffers :: IORef [Ptr ()],
    audioBufferAliases :: IORef [Ptr ()],
    automationEventLists :: IORef [Ptr ()],
    funPtrs :: IORef [FunPtr ()]
  }

defaultWindowResources :: IO WindowResources
defaultWindowResources = do
  sIds <- newIORef []
  sLocs <- newIORef Map.empty
  tIds <- newIORef []
  fbs <- newIORef []
  vaos <- newIORef []
  vbos <- newIORef []
  cdps <- newIORef []
  aBufs <- newIORef []
  aliases <- newIORef []
  eventLists <- newIORef []
  fPtrs <- newIORef []
  return
    WindowResources
      { shaderIds = sIds,
        shaderLocations = sLocs,
        textureIds = tIds,
        frameBuffers = fbs,
        vaoIds = vaos,
        vboIds = vbos,
        ctxDataPtrs = cdps,
        audioBuffers = aBufs,
        audioBufferAliases = aliases,
        automationEventLists = eventLists,
        funPtrs = fPtrs
      }

$( genNative
     [ ("c'rlGetShaderIdDefault", "rlGetShaderIdDefault_", "rlgl_bindings.h", [t|IO CUInt|], False),
       ("c'rlUnloadShaderProgram", "rlUnloadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
       ("c'rlUnloadTexture", "rlUnloadTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
       ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
       ("c'rlUnloadVertexArray", "rlUnloadVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
       ("c'rlUnloadVertexBuffer", "rlUnloadVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
       ("c'unloadMusicStreamData", "UnloadMusicStreamData", "rl_internal.h", [t|CInt -> Ptr () -> IO ()|], False),
       ("c'unloadAudioBuffer", "UnloadAudioBuffer_", "rl_internal.h", [t|Ptr () -> IO ()|], False),
       ("c'unloadAudioBufferAlias", "UnloadAudioBufferAlias", "rl_internal.h", [t|Ptr () -> IO ()|], False),
       ("c'getPixelDataSize", "GetPixelDataSize_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> IO CInt|], False)
     ]
 )

unloadSingleShader :: (Integral a) => a -> WindowResources -> IO ()
unloadSingleShader sId' wr = do
  shaderIdDefault <- c'rlGetShaderIdDefault
  unless (sId == shaderIdDefault) (c'rlUnloadShaderProgram sId)
  modifyIORef (shaderIds wr) (delete sId)
  where
    sId = fromIntegral sId'

unloadSingleTexture :: (Integral a) => a -> WindowResources -> IO ()
unloadSingleTexture tId' wr = do
  when (tId > 0) (c'rlUnloadTexture tId)
  modifyIORef (textureIds wr) (delete tId)
  where
    tId = fromIntegral tId'

unloadSingleFrameBuffer :: (Integral a) => a -> WindowResources -> IO ()
unloadSingleFrameBuffer fbId' wr = do
  when (fbId > 0) (c'rlUnloadFramebuffer fbId)
  modifyIORef (frameBuffers wr) (delete fbId)
  where
    fbId = fromIntegral fbId'

unloadSingleVaoId :: (Integral a) => a -> WindowResources -> IO ()
unloadSingleVaoId vaoId' wr = do
  c'rlUnloadVertexArray vaoId
  modifyIORef (vaoIds wr) (delete vaoId)
  where
    vaoId = fromIntegral vaoId'

unloadSingleVboIdList :: (Integral a) => Maybe [a] -> WindowResources -> IO ()
unloadSingleVboIdList Nothing _ = return ()
unloadSingleVboIdList (Just vboIdList') wr = do
  forM_
    vboIdList
    ( \vboId -> do
        c'rlUnloadVertexBuffer vboId
        modifyIORef (vboIds wr) (delete vboId)
    )
  where
    vboIdList = map fromIntegral vboIdList'

unloadSingleCtxDataPtr :: (Integral a) => a -> Ptr () -> WindowResources -> IO ()
unloadSingleCtxDataPtr ctxType' ctxData wr = do
  c'unloadMusicStreamData ctxType ctxData
  modifyIORef (ctxDataPtrs wr) (delete (ctxType, ctxData))
  where
    ctxType = fromIntegral ctxType'

unloadSingleAudioBuffer :: Ptr () -> WindowResources -> IO ()
unloadSingleAudioBuffer buffer wr = do
  c'unloadAudioBuffer buffer
  modifyIORef (audioBuffers wr) (delete buffer)

unloadSingleAudioBufferAlias :: Ptr () -> WindowResources -> IO ()
unloadSingleAudioBufferAlias buffer wr = do
  c'unloadAudioBufferAlias buffer
  modifyIORef (audioBufferAliases wr) (delete buffer)

unloadSingleAutomationEventList :: Ptr () -> WindowResources -> IO ()
unloadSingleAutomationEventList eventList wr = do
  _unloadAutomationEventList eventList
  modifyIORef (automationEventLists wr) (delete eventList)

unloadSingleFunPtr :: FunPtr () -> WindowResources -> IO ()
unloadSingleFunPtr fPtr wr = do
  freeHaskellFunPtr fPtr
  modifyIORef (funPtrs wr) (delete fPtr)

unloadShaders :: WindowResources -> IO ()
unloadShaders wr = do
  shaderIdDefault <- c'rlGetShaderIdDefault
  vals <- readIORef (shaderIds wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals (\sId -> unless (sId == shaderIdDefault) (c'rlUnloadShaderProgram sId))
        putStrLn $ "INFO: SHADER: h-raylib successfully auto-unloaded shaders (" ++ show l ++ " in total)"
    )

unloadTextures :: WindowResources -> IO ()
unloadTextures wr = do
  vals <- readIORef (textureIds wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals (\tId -> when (tId > 0) (c'rlUnloadTexture tId))
        putStrLn $ "INFO: TEXTURE: h-raylib successfully auto-unloaded textures (" ++ show l ++ " in total)"
    )

unloadFrameBuffers :: WindowResources -> IO ()
unloadFrameBuffers wr = do
  vals <- readIORef (frameBuffers wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals (\fbId -> when (fbId > 0) (c'rlUnloadFramebuffer fbId))
        putStrLn $ "INFO: FBO: h-raylib successfully auto-unloaded frame buffers (" ++ show l ++ " in total)"
    )

unloadVaoIds :: WindowResources -> IO ()
unloadVaoIds wr = do
  vals <- readIORef (vaoIds wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals c'rlUnloadVertexArray
        putStrLn $ "INFO: VAO: h-raylib successfully auto-unloaded vertex arrays (" ++ show l ++ " in total)"
    )

unloadVboIds :: WindowResources -> IO ()
unloadVboIds wr = do
  vals <- readIORef (vboIds wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals c'rlUnloadVertexBuffer
        putStrLn $ "INFO: VBO: h-raylib successfully auto-unloaded vertex buffers (" ++ show l ++ " in total)"
    )

unloadCtxData :: WindowResources -> IO ()
unloadCtxData wr = do
  vals <- readIORef (ctxDataPtrs wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals $ uncurry c'unloadMusicStreamData
        putStrLn $ "INFO: AUDIO: h-raylib successfully auto-unloaded music data (" ++ show l ++ " in total)"
    )

unloadAudioBuffers :: WindowResources -> IO ()
unloadAudioBuffers wr = do
  vals <- readIORef (audioBuffers wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals c'unloadAudioBuffer
        putStrLn $ "INFO: AUDIO: h-raylib successfully auto-unloaded audio buffers (" ++ show l ++ " in total)"
    )

unloadAudioBufferAliases :: WindowResources -> IO ()
unloadAudioBufferAliases wr = do
  vals <- readIORef (audioBufferAliases wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals c'unloadAudioBufferAlias
        putStrLn $ "INFO: AUDIO: h-raylib successfully auto-unloaded audio buffer aliases (" ++ show l ++ " in total)"
    )

unloadAutomationEventLists :: WindowResources -> IO ()
unloadAutomationEventLists wr = do
  vals <- readIORef (automationEventLists wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals _unloadAutomationEventList
        putStrLn $ "INFO: AUTOMATION: h-raylib successfully auto-unloaded automation event lists (" ++ show l ++ " in total)"
    )

unloadFunPtrs :: WindowResources -> IO ()
unloadFunPtrs wr = do
  vals <- readIORef (funPtrs wr)
  let l = length vals
  when
    (l > 0)
    ( do
        forM_ vals freeHaskellFunPtr
        putStrLn $ "INFO: h-raylib successfully auto-unloaded `FunPtr`s (" ++ show l ++ " in total)"
    )

addShaderId :: (Integral a) => a -> WindowResources -> IO ()
addShaderId sId' wr = do
  modifyIORef (shaderIds wr) (\xs -> if sId `elem` xs then xs else sId : xs)
  where
    sId = fromIntegral sId'

addTextureId :: (Integral a) => a -> WindowResources -> IO ()
addTextureId tId' wr = do
  modifyIORef (textureIds wr) (\xs -> if tId `elem` xs then xs else tId : xs)
  where
    tId = fromIntegral tId'

addFrameBuffer :: (Integral a) => a -> WindowResources -> IO ()
addFrameBuffer fbId' wr = do
  modifyIORef (frameBuffers wr) (\xs -> if fbId `elem` xs then xs else fbId : xs)
  where
    fbId = fromIntegral fbId'

addVaoId :: (Integral a) => a -> WindowResources -> IO ()
addVaoId vaoId' wr = do
  modifyIORef (vaoIds wr) (\xs -> if vaoId `elem` xs then xs else vaoId : xs)
  where
    vaoId = fromIntegral vaoId'

addVboIds :: (Integral a) => Maybe [a] -> WindowResources -> IO ()
addVboIds Nothing _ = return ()
addVboIds (Just bIds') wr = do
  forM_ bIds (\x -> modifyIORef (vboIds wr) (\xs -> if x `elem` xs then xs else x : xs))
  where
    bIds = map fromIntegral bIds'

addCtxData :: (Integral a) => a -> Ptr () -> WindowResources -> IO ()
addCtxData ctxType' ctxData wr = do
  modifyIORef (ctxDataPtrs wr) (\xs -> if (ctxType, ctxData) `elem` xs then xs else (ctxType, ctxData) : xs)
  where
    ctxType = fromIntegral ctxType'

addAudioBuffer :: Ptr () -> WindowResources -> IO ()
addAudioBuffer buffer wr = do
  modifyIORef (audioBuffers wr) (\xs -> if buffer `elem` xs then xs else buffer : xs)

addAudioBufferAlias :: Ptr () -> WindowResources -> IO ()
addAudioBufferAlias alias wr = do
  modifyIORef (audioBufferAliases wr) (\xs -> if alias `elem` xs then xs else alias : xs)

addAutomationEventList :: Ptr () -> WindowResources -> IO ()
addAutomationEventList eventList wr = do
  modifyIORef (automationEventLists wr) (\xs -> if eventList `elem` xs then xs else eventList : xs)

addFunPtr :: FunPtr () -> WindowResources -> IO ()
addFunPtr fPtr wr = do
  modifyIORef (funPtrs wr) (\xs -> if fPtr `elem` xs then xs else fPtr : xs)

_unloadAutomationEventList :: Ptr () -> IO ()
_unloadAutomationEventList ptr = (free =<< (peekByteOff ptr 8 :: IO (Ptr ()))) >> free ptr

getPixelDataSize :: Int -> Int -> Int -> Int
getPixelDataSize width height format = unsafePerformIO (fromIntegral <$> c'getPixelDataSize (fromIntegral width) (fromIntegral height) (fromIntegral format))