Skip to content

Instantly share code, notes, and snippets.

@nsf
Last active August 29, 2015 14:26
Show Gist options
  • Save nsf/05e444da21de4461acc2 to your computer and use it in GitHub Desktop.
Save nsf/05e444da21de4461acc2 to your computer and use it in GitHub Desktop.
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