Created
January 12, 2012 21:06
-
-
Save deneuxj/1603109 to your computer and use it in GitHub Desktop.
Parallel draw/update game component for XNA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace CleverRake.XnaUtils | |
open Microsoft.Xna.Framework | |
open System.Threading | |
type IFramePerSecondCounter = | |
abstract FramesPerSecond : float | |
/// An update-able and drawable game component which performs light updates on the main thread, | |
/// then draws on a separate thread in parallel of more computation-heavy updates. | |
/// initialize_fun is called when assets are loaded. | |
/// dispose is called when the component is disposed, and should be used to unload assets. | |
/// update_fun takes a GameTime and a state, and should produce draw data and computation data. | |
/// draw_fun takes a game time and draw data and should return nothing. | |
/// compute_fun takes a game time and computation data and should return a new state. | |
type ParallelUpdateDrawGameComponent<'State, 'DrawData, 'ComputationData> | |
(game, | |
initial_state : 'State, | |
initialize_fun : unit -> unit, | |
update_fun : GameTime -> 'State -> 'DrawData * 'ComputationData, | |
compute_fun : GameTime -> 'ComputationData -> 'State, | |
draw_fun : GameTime -> 'DrawData -> unit, | |
dispose : unit -> unit) = | |
inherit DrawableGameComponent(game) | |
let mutable state = initial_state | |
let mutable draw_data = Unchecked.defaultof<'DrawData> | |
let mutable compute_data = Unchecked.defaultof<'ComputationData> | |
let mutable gt_shared = GameTime() | |
let mutable enabled = true | |
let mutable update_order = 0 | |
let signal_start = new AutoResetEvent(false) | |
let mutable kill_requested = false | |
let signal_done = new AutoResetEvent(false) | |
let do_compute() = | |
#if XBOX360 | |
// Set affinity | |
Thread.CurrentThread.SetProcessorAffinity(3) // 0 and 2 are reserved, I assume the "main" thread is 1. | |
#endif | |
while not kill_requested do | |
signal_start.WaitOne() |> ignore | |
state <- compute_fun gt_shared compute_data | |
signal_done.Set() |> ignore | |
let compute_thread = new Thread(new ThreadStart(do_compute)) | |
// Must be called from the main thread. | |
let post_compute_then_draw gt = | |
if not kill_requested then | |
let state = state | |
gt_shared <- gt | |
signal_start.Set() |> ignore | |
draw_fun gt draw_data | |
signal_done.WaitOne() |> ignore | |
let mutable frameCounter = 0 | |
let mutable timeCounter = 0.0 | |
let mutable fps = 0.0 | |
let fpsUpdatePeriod = 0.3 | |
do | |
compute_thread.IsBackground <- true | |
compute_thread.Start() | |
override this.Initialize() = | |
base.Initialize() | |
initialize_fun() | |
override this.Update(gt) = | |
if base.Enabled then | |
base.Update(gt) | |
let draw, compute = update_fun gt state | |
draw_data <- draw | |
compute_data <- compute | |
override this.Draw(gt) = | |
if base.Visible then | |
base.Draw(gt) | |
post_compute_then_draw gt | |
else | |
state <- compute_fun gt compute_data | |
timeCounter <- timeCounter + gt.ElapsedGameTime.TotalSeconds | |
frameCounter <- frameCounter + 1 | |
if timeCounter > fpsUpdatePeriod then | |
fps <- float frameCounter / timeCounter | |
timeCounter <- timeCounter - fpsUpdatePeriod | |
frameCounter <- 0 | |
interface System.IDisposable with | |
member this.Dispose() = | |
base.Dispose() | |
dispose() | |
signal_start.Dispose() | |
signal_done.Dispose() | |
// member this.Dispose() = () // DrawableGameComponent.Dispose() is sealed. Thank you very much... | |
interface IFramePerSecondCounter with | |
member this.FramesPerSecond = fps | |
member this.FramesPerSecond = fps | |
member this.RequestKill() = | |
kill_requested <- false | |
signal_start.Set() |> ignore | |
member this.WaitUntilDead() = | |
compute_thread.Join() | |
member this.IsDead = not(compute_thread.IsAlive) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment