Last active
August 29, 2015 14:26
-
-
Save nsf/05e444da21de4461acc2 to your computer and use it in GitHub Desktop.
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
module NG.FS.ResourceCache | |
open NG | |
open NG.FS.Coroutines | |
open System | |
open System.Collections.Generic | |
type private SharedResource<'T> (r : 'T) = | |
member val Mutex = Mutex() | |
member val Loaded = false with get, set | |
member val Resource = r | |
let inline private resourceDict<'T> () = Dictionary<string, SharedResource<'T>>() | |
let private resourceCacheMutex = Mutex() | |
let private fonts = resourceDict<C.FontResource>() | |
let private textures = resourceDict<C.TextureResource>() | |
let private atlases = resourceDict<C.AtlasResource>() | |
let private cursors = resourceDict<C.CursorResource>() | |
let inline private loadData res path = (^T : (member LoadData : string -> unit) res, path) | |
let inline private setResource msg res = (^T : (member Resource : 'U with set) msg, res) | |
let inline private new'<'T when 'T : (static member New : unit -> 'T)> () = (^T : (static member New : unit -> 'T)()) | |
let inline private twoStepLoad<'R, 'M when 'R : (member LoadData : string -> unit) | |
and 'M :> C.BindGen.IMessage | |
and 'M : (static member New : unit -> 'M) | |
and 'M : (member Resource : 'R with set)> (path : string) (res : 'R) = | |
routine { | |
do! coroutine_p (Type = Type.IO) { loadData res path } | |
let mutable msg = new'<'M>() | |
setResource msg res | |
do! sendMessageAndDispose msg | |
} | |
let private splitAtlasPath (path : string) = | |
let paths = path.Split('#') | |
if paths.Length <> 2 then | |
failwithf "Bad atlas texture path: '%s', there must be exactly one separator symbol '#'" path | |
let file = paths.[0].Trim() | |
let texture = paths.[1].Trim() | |
if file.Length = 0 then | |
failwithf "Bad atlas texture path: '%s', there must be a non-empty file path" path | |
if texture.Length = 0 then | |
failwithf "Bad atlas texture path: '%s', there must be a non-empty texture path" path | |
file, texture | |
let private resolveAtlasTexture (texturePath : string) (atlas : C.AtlasResource) = | |
atlas.Get(texturePath) | |
let inline private getOrAdd (d : Dictionary<string, SharedResource<'T>>) (path : string) = | |
let ok, res = d.TryGetValue(path) | |
if ok then | |
res | |
else | |
let res = SharedResource(new'<'T>()) | |
d.Add(path, res) | |
res | |
let inline private resolveResource (d : Dictionary<string, SharedResource<'T>>) | |
(path : string) | |
(loader : string -> 'T -> Routine<unit>) | |
(resolver : 'T -> 'U) = | |
routine { | |
do! lockMutex resourceCacheMutex | |
let r = getOrAdd d path | |
do! unlockMutex resourceCacheMutex | |
do! lockMutex r.Mutex | |
if not r.Loaded then | |
do! loader path r.Resource | |
r.Loaded <- true | |
let result = resolver r.Resource | |
do! unlockMutex r.Mutex | |
return result | |
} | |
let resolveFontResource (path : string) : Routine<C.FontResource> = routine { | |
return! resolveResource fonts path twoStepLoad<C.FontResource, C.Messages.UploadFontResource> id | |
} | |
let resolveTextureResource (path : string) : Routine<C.TextureResource> = routine { | |
return! resolveResource textures path twoStepLoad<C.TextureResource, C.Messages.UploadTextureResource> id | |
} | |
let resolveAtlasTextureResource (path : string) : Routine<C.AtlasTexture> = routine { | |
let file, texture = splitAtlasPath path | |
return! resolveResource atlases file twoStepLoad<C.AtlasResource, C.Messages.UploadAtlasResource> (resolveAtlasTexture texture) | |
} | |
let resolveCursorResource (path : string) : Routine<C.CursorResource> = routine { | |
return! resolveResource cursors path twoStepLoad<C.CursorResource, C.Messages.UploadCursorResource> id | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment