packages feed

Box2D-0.1.0.0: bench/Bench.hs

{-# LANGUAGE PatternSynonyms #-}

module Main (main) where

import Control.Monad (forM_)

import Test.Tasty.Bench

import Box2D.Body (BodyDef (..), defaultBodyDef, pattern DynamicBody)
import Box2D.Body qualified as Body
import Box2D.Geometry (Circle (..), Segment (..))
import Box2D.Id (WorldId)
import Box2D.MathTypes
import Box2D.Shape (defaultShapeDef)
import Box2D.Shape qualified as Shape
import Box2D.World (WorldDef (..), defaultWorldDef)
import Box2D.World qualified as World

{- | Build a world holding @n@ dynamic circles dropped onto a static ground
segment, so that a step exercises broad phase, narrow phase and the solver.
-}
setupWorld :: Int -> IO WorldId
setupWorld n = do
  wd <- defaultWorldDef
  -- keep the pile awake: a settled world would otherwise fall asleep and
  -- the stepping benchmarks would measure an idle no-op step
  w <- World.create wd{gravity = Vec2 0 (-10), enableSleep = 0}

  gd <- defaultBodyDef
  ground <- Body.create w gd
  gsd <- defaultShapeDef
  _ <- Shape.createSegment ground gsd (Segment (Vec2 (-40) 0) (Vec2 40 0))

  bd <- defaultBodyDef
  sd <- defaultShapeDef
  forM_ [0 .. n - 1] $ \i -> do
    let
      col = fromIntegral (i `mod` 16)
      row = fromIntegral (i `div` 16)
      position = Pos (col - 8) (2 + row)
    b <- Body.create w bd{type_ = DynamicBody, position = position}
    _ <- Shape.createCircle b sd (Circle vec2Zero 0.5)
    pure ()

  -- let the pile settle so steps hit a steady contact load
  forM_ [1 .. 120 :: Int] $ \_ -> World.step w (1 / 60) 4
  pure w

main :: IO ()
main = do
  emptyDef <- defaultWorldDef
  world64 <- setupWorld 64
  world256 <- setupWorld 256
  defaultMain
    [ bench "World.create + World.destroy" $
        whnfIO (World.create emptyDef >>= World.destroy)
    , bench "step 64 circles" $
        whnfIO (World.step world64 (1 / 60) 4)
    , bench "step 256 circles" $
        whnfIO (World.step world256 (1 / 60) 4)
    ]