Merge commit '1b8d754cd64f3a3b94dbf41a04a30b456a75230e' into resourcetify

This commit is contained in:
nek0 2022-07-10 04:23:01 +02:00
commit 3ca8e62367
6 changed files with 142 additions and 80 deletions

View file

@ -102,7 +102,7 @@ library
ghc-options: -Wall ghc-options: -Wall
-- Other library packages from which modules are imported. -- Other library packages from which modules are imported.
build-depends: base >=4.9 && < 5 build-depends: base >=4.9 && < 5
, sdl2 ^>= 2.5 , sdl2 >= 2.5
, linear , linear
, text , text
, mtl , mtl
@ -117,6 +117,7 @@ library
, stm , stm
, uuid , uuid
, vector , vector
, resourcet
-- This example shows the message system. only makes sense when compiling with -- This example shows the message system. only makes sense when compiling with
-- verbose flag. -- verbose flag.
@ -129,7 +130,7 @@ executable example00
if flag(examples) if flag(examples)
build-depends: base >=4.9 && < 5 build-depends: base >=4.9 && < 5
, affection , affection
, sdl2 ^>= 2.5 , sdl2 >= 2.5
, stm , stm
else else
buildable: False buildable: False
@ -145,7 +146,7 @@ executable example01
if flag(examples) if flag(examples)
build-depends: base >=4.9 && < 5 build-depends: base >=4.9 && < 5
, affection , affection
, sdl2 ^>= 2.5 , sdl2 >= 2.5
, stm , stm
, OpenGL , OpenGL
, random , random
@ -168,7 +169,7 @@ executable example02
if flag(examples) if flag(examples)
build-depends: base >=4.9 && < 5 build-depends: base >=4.9 && < 5
, affection , affection
, sdl2 ^>= 2.5 , sdl2 >= 2.5
, stm , stm
, OpenGL , OpenGL
, random , random

View file

@ -1,4 +1,4 @@
constraints: affection +verbose constraints: affection +verbose +examples
profiling: true profiling: true
packages: packages:
./. ./.

2
extern/affection/hie.yaml vendored Normal file
View file

@ -0,0 +1,2 @@
cradle:
cabal:

View file

@ -1,6 +1,5 @@
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE Strict #-} {-# LANGUAGE Strict #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
@ -23,12 +22,14 @@ import qualified Graphics.GL as GLRaw
import Foreign.Marshal.Array import Foreign.Marshal.Array
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import Data.String (fromString)
import System.Clock import System.Clock
import Control.Monad.Loops import Control.Monad.Loops
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Control.Monad.Trans.Resource
-- internal imports -- internal imports
@ -46,46 +47,83 @@ withAffection
:: forall us. (Affectionate us) :: forall us. (Affectionate us)
=> AffectionConfig us -- ^ Configuration of the Game and its engine. => AffectionConfig us -- ^ Configuration of the Game and its engine.
-> IO () -> IO ()
withAffection AffectionConfig{..} = do withAffection AffectionConfig{..} = runResourceT $ do
liftIO $ logIO Debug "Affection starting" liftIO $ logIO Debug "Affection starting"
liftIO $ logIO Debug "Initializing SDL" liftIO $ logIO Debug "Initializing SDL"
-- intialiaze SDL -- intialiaze SDL
case initComponents of case initComponents of
All -> All ->
SDL.initializeAll SDL.initializeAll
Only is -> Only is ->
SDL.initialize is SDL.initialize is
-- give SDL render quality -- give SDL render quality
SDL.HintRenderScaleQuality SDL.$= SDL.ScaleLinear SDL.HintRenderScaleQuality SDL.$= SDL.ScaleLinear
-- just checking… -- just checking…
do
renderQuality <- SDL.get SDL.HintRenderScaleQuality renderQuality <- SDL.get SDL.HintRenderScaleQuality
when (renderQuality /= SDL.ScaleLinear) $ when (renderQuality /= SDL.ScaleLinear) $
logIO Warn "Linear texture filtering not enabled!" liftIO $ logIO Warn "Linear texture filtering not enabled!"
void $ liftIO (logIO Debug . fromString . show <$> (SDL.version :: IO (Integer, Integer, Integer)))
-- construct window -- construct window
liftIO $ logIO Debug "Creating Window(s)" liftIO $ logIO Debug "Creating Window(s)"
windows <- zip3 (map (\(x,_,_) -> x) windowConfigs) <$> windows <-
mapM mapM
(\wc -> SDL.createWindow windowTitle ((\(_,y,_) -> y) wc)) (\(_, sdlWindowConfig, mode) -> do
windowConfigs <*> (windowKey, window) <-
pure (map (\(_,_,z) -> z) windowConfigs) allocate
(SDL.createWindow windowTitle sdlWindowConfig)
(\window -> do
logIO Debug "Destroying Window"
SDL.destroyWindow window
)
return $ AffectionWindow window windowKey mode
)
windowConfigs
-- Show windows
mapM_ (SDL.showWindow . awWindow) windows
-- set modes of windows
mapM_ (\(AffectionWindow window _ mode) -> SDL.setWindowMode window mode) windows
-- Make GL context shareable
void $ SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
-- Create OpenGL contexts
contexts <-
mapM
(\(AffectionWindow window _ _) -> do
(contextKey, context) <-
allocate
(SDL.glCreateContext window)
(\context -> do
logIO Debug "Destroying context"
SDL.glDeleteContext context
)
return $ AffectionContext context contextKey
)
windows
mapM_ (SDL.showWindow . (\(_,y,_) -> y)) windows
_ <- SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
contexts <- zip (map (\(x,_,_) -> x) windows) <$>
mapM (SDL.glCreateContext . (\(_,y,_) -> y)) windows
mapM_ (\w -> flip SDL.setWindowMode ((\(_,_,z) -> z) w) ((\(_,y,_) -> y) w)) windows
-- sync updates with monitor -- sync updates with monitor
-- SDL.swapInterval $= SDL.SynchronizedUpdates -- <- causes Problems with windows -- SDL.swapInterval $= SDL.SynchronizedUpdates -- <- causes Problems with windows
liftIO $ logIO Debug "Getting Time"
-- print current used GL Version -- print current used GL Version
version <- peekArray0 (0 :: Word8) =<< GLRaw.glGetString GLRaw.GL_VERSION version <- liftIO $ peekArray0 (0 :: Word8) =<< GLRaw.glGetString GLRaw.GL_VERSION
print (B.pack version) liftIO $ print (B.pack version)
-- get current time -- get current time
execTime <- getTime Monotonic liftIO $ logIO Debug "Getting Time"
execTime <- liftIO $ getTime Monotonic
liftIO $ logIO Debug "Loading initial data container" liftIO $ logIO Debug "Loading initial data container"
-- construct game data object from provided Affectionate instance -- construct game data object from provided Affectionate instance
gameData <- loadState @us (gameDataKey, gameData) <-
allocate
(liftIO $ loadState @us)
(liftIO . cleanUp)
-- build state container -- build state container
let initContainer = AffectionData let initContainer = AffectionData
{ drawWindows = windows { drawWindows = windows
@ -95,11 +133,12 @@ withAffection AffectionConfig{..} = do
, sysTime = execTime , sysTime = execTime
, pausedTime = False , pausedTime = False
} }
-- initialize and run state -- initialize and run state
(_, _) <- runStateT void $ liftIO $ runAffection initContainer
(A.runState
(do (do
liftIO $ logIO Debug "Running Pre-Loop stage" liftIO $ logIO Debug "Running Pre-Loop stage"
-- run preLoop function from Affectionate -- run preLoop function from Affectionate
preLoop gameData preLoop gameData
liftIO $ logIO Debug "Starting Loop" liftIO $ logIO Debug "Starting Loop"
@ -107,31 +146,40 @@ withAffection AffectionConfig{..} = do
(do (do
-- get state -- get state
ad <- get ad <- get
-- Measure time difference form last run -- Measure time difference form last run
now <- liftIO $ getTime Monotonic now <- liftIO $ getTime Monotonic
let lastTime = sysTime ad let lastTime = sysTime ad
-- compute dt and update elapsedTime -- compute dt and update elapsedTime
let dt = fromIntegral let dt = fromIntegral
(toNanoSecs $ diffTimeSpec lastTime now) / (10 ^ (9 :: Int)) (toNanoSecs $ diffTimeSpec lastTime now) / (10 ^ (9 :: Int))
ne = elapsedTime ad + dt ne = elapsedTime ad + dt
-- update state data object with new time values -- update state data object with new time values
put $ ad put $ ad
{ elapsedTime = ne { elapsedTime = ne
, deltaTime = dt , deltaTime = dt
} }
-- poll events -- poll events
liftIO SDL.pumpEvents liftIO SDL.pumpEvents
evs <- preHandleEvents =<< liftIO SDL.pollEvents evs <- preHandleEvents =<< liftIO SDL.pollEvents
-- handle events -- handle events
handleEvents gameData evs handleEvents gameData evs
-- execute user defined update loop -- execute user defined update loop
unless (pausedTime ad) (update gameData dt) unless (pausedTime ad) (update gameData dt)
-- clear GL buffer >> execute user defined draw loop >> flush GL buffer -- clear GL buffer >> execute user defined draw loop >> flush GL buffer
liftIO $ GL.clear [GL.ColorBuffer, GL.DepthBuffer, GL.StencilBuffer] liftIO $ GL.clear [GL.ColorBuffer, GL.DepthBuffer, GL.StencilBuffer]
draw gameData draw gameData
liftIO GL.flush liftIO GL.flush
-- actual displaying of newly drawn frame -- actual displaying of newly drawn frame
mapM_ (SDL.glSwapWindow . (\(_,y,_) -> y)) windows mapM_ (SDL.glSwapWindow . awWindow) windows
-- save new time -- save new time
ad3 <- get ad3 <- get
when (sysTime ad == sysTime ad3) ( when (sysTime ad == sysTime ad3) (
@ -141,11 +189,16 @@ withAffection AffectionConfig{..} = do
) )
) )
) )
) initContainer -- Cleanup works
liftIO $ logIO Debug "Loop ended. Cleaning" liftIO $ logIO Debug "Loop ended. Cleaning"
cleanUp gameData release gameDataKey
liftIO $ logIO Debug "Destroying Window" -- mapM_ (SDL.glDeleteContext . snd) contexts
mapM_ (SDL.glDeleteContext . snd) contexts -- mapM_ (SDL.destroyWindow . (\(_,y,_) -> y)) windows
mapM_ (SDL.destroyWindow . (\(_,y,_) -> y)) windows
-- SDL.quit -- <- This causes segfaults depending on hardware -- SDL.quit -- <- This causes segfaults depending on hardware
liftIO $ logIO Debug "This is the end" liftIO $ logIO Debug "This is the end"
runAffection
:: AffectionData
-> AffectionState AffectionData ResIO a
-> IO (a, AffectionData)
runAffection initialState a = runResourceT $ runStateT (A.runState a) initialState

View file

@ -5,6 +5,8 @@ module Affection.Types
, AffectionData(..) , AffectionData(..)
, AffectionStateInner , AffectionStateInner
, AffectionState(..) , AffectionState(..)
, AffectionWindow(..)
, AffectionContext(..)
, InitComponents(..) , InitComponents(..)
, Angle , Angle
-- | SDL reexports -- | SDL reexports
@ -23,6 +25,7 @@ import qualified Data.Text as T
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Control.Monad.Trans.Resource
import qualified Control.Monad.Parallel as MP import qualified Control.Monad.Parallel as MP
import System.Clock (TimeSpec) import System.Clock (TimeSpec)
@ -50,19 +53,8 @@ data InitComponents
-- | Main type for defining the look, feel and action of the whole application. -- | Main type for defining the look, feel and action of the whole application.
data AffectionData = AffectionData data AffectionData = AffectionData
{ drawWindows :: { drawWindows :: [ AffectionWindow ] -- ^ SDL windows
[ , glContext :: [ AffectionContext ] -- ^ OpenGL rendering contexts
( Word -- --^ Window identifier
, SDL.Window -- --^ Window linked with identifier
, SDL.WindowMode -- -- ^ current screen mode
)
] -- ^ SDL windows
, glContext ::
[
( Word -- --^ Window identifier
, SDL.GLContext -- --^ Associated OpenGL context
)
] -- ^ OpenGL rendering contexts
, elapsedTime :: Double -- ^ Elapsed time in seconds , elapsedTime :: Double -- ^ Elapsed time in seconds
, deltaTime :: Double -- ^ Elapsed time in seconds since last tick , deltaTime :: Double -- ^ Elapsed time in seconds since last tick
, sysTime :: TimeSpec -- ^ System time (NOT the time on the clock) , sysTime :: TimeSpec -- ^ System time (NOT the time on the clock)
@ -70,15 +62,26 @@ data AffectionData = AffectionData
} }
-- | Inner 'StateT' monad for the update state -- | Inner 'StateT' monad for the update state
type AffectionStateInner sd a = StateT sd a type AffectionStateInner sd m = StateT sd m
-- | Affection's state monad -- | Affection's state monad
newtype AffectionState sd m a = AffectionState newtype AffectionState sd m a = AffectionState
{ runState :: AffectionStateInner sd m a } { runState :: AffectionStateInner sd m a }
deriving (Functor, Applicative, Monad, MonadIO, MonadState sd) deriving (Functor, Applicative, Monad, MonadIO, MonadState sd, MonadResource)
instance MP.MonadParallel m => MP.MonadParallel (AffectionState sd m) instance MP.MonadParallel m => MP.MonadParallel (AffectionState sd m)
type Affection a = AffectionState AffectionData IO a type Affection a = AffectionState AffectionData ResIO a
type Angle = Double type Angle = Double
data AffectionWindow = AffectionWindow
{ awWindow :: SDL.Window
, awReleaseKey :: ReleaseKey
, awMode :: SDL.WindowMode
}
data AffectionContext = AffectionContext
{ acContext :: SDL.GLContext
, acReleaseKey :: ReleaseKey
}

View file

@ -39,31 +39,34 @@ getDelta = gets deltaTime
toggleScreen :: Word -> Affection () toggleScreen :: Word -> Affection ()
toggleScreen windowIdent = do toggleScreen windowIdent = do
ad <- get ad <- get
let mwindow = find (\(ident, _, _) -> ident == windowIdent) (drawWindows ad) (stop, alteredWindowList) <- foldM
case mwindow of (\(stop, resWindows) (num, aw@(AffectionWindow window _ mode)) -> do
Just (ident, window, mode) -> do if stop || num == windowIdent
then do
newMode <- case mode of newMode <- case mode of
SDL.FullscreenDesktop -> do SDL.FullscreenDesktop -> do
SDL.setWindowMode window SDL.Windowed liftIO $ SDL.setWindowMode window SDL.Windowed
return SDL.Windowed return SDL.Windowed
SDL.Windowed -> do SDL.Windowed -> do
SDL.setWindowMode window SDL.FullscreenDesktop liftIO $ SDL.setWindowMode window SDL.FullscreenDesktop
return SDL.FullscreenDesktop return SDL.FullscreenDesktop
x -> do x -> do
liftIO $ logIO Warn ("Unexpected window mode: " <> fromString (show x)) liftIO $ logIO Warn ("Unexpected window mode: " <> fromString (show x))
return x return x
return (True, resWindows ++ [aw { awMode = newMode }])
else
return (stop, resWindows ++ [aw])
)
(False, [])
(zip [0..] (drawWindows ad))
if stop
then do
now <- liftIO $ getTime Monotonic now <- liftIO $ getTime Monotonic
put ad put ad
{ sysTime = now { sysTime = now
, drawWindows = map , drawWindows = alteredWindowList
(\e@(lid, win, _) ->
if lid == ident
then (lid, win, newMode)
else e
)
(drawWindows ad)
} }
Nothing -> do else
liftIO $ logIO Warn ("No window found with ident " <> fromString (show windowIdent)) liftIO $ logIO Warn ("No window found with ident " <> fromString (show windowIdent))
-- | Fit the GL Viewport to Window size -- | Fit the GL Viewport to Window size