Main repository of MikuMikuStudio
Revision | 69a5976b653746b019085207def7e4691aa9a717 (tree) |
---|---|
Time | 2013-07-07 03:56:44 |
Author | kobayasi <kobayasi@pscn...> |
Commiter | kobayasi |
add asset cache
@@ -61,7 +61,16 @@ public class AssetKey<T> implements Savable { | ||
61 | 61 | public AssetKey(){ |
62 | 62 | } |
63 | 63 | |
64 | - protected static String getExtension(String name){ | |
64 | + @Override | |
65 | + public AssetKey<T> clone() { | |
66 | + try { | |
67 | + return (AssetKey<T>) super.clone(); | |
68 | + } catch (CloneNotSupportedException ex) { | |
69 | + throw new AssertionError(); | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + protected static String getExtension(String name) { | |
65 | 74 | int idx = name.lastIndexOf('.'); |
66 | 75 | //workaround for filenames ending with xml and another dot ending before that (my.mesh.xml) |
67 | 76 | if (name.toLowerCase().endsWith(".xml")) { |
@@ -0,0 +1,71 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Redistribution and use in source and binary forms, with or without | |
6 | + * modification, are permitted provided that the following conditions are | |
7 | + * met: | |
8 | + * | |
9 | + * * Redistributions of source code must retain the above copyright | |
10 | + * notice, this list of conditions and the following disclaimer. | |
11 | + * | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * | |
16 | + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | |
17 | + * may be used to endorse or promote products derived from this software | |
18 | + * without specific prior written permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
22 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
27 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
28 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
29 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + */ | |
32 | +package com.jme3.asset; | |
33 | + | |
34 | +import com.jme3.material.Material; | |
35 | +import com.jme3.shader.Shader; | |
36 | + | |
37 | +/** | |
38 | + * <code>AssetProcessor</code> is used to apply processing to assets | |
39 | + * after they have been loaded. They are assigned to a particular | |
40 | + * asset type (which is represented by a {@link Class} and any assets | |
41 | + * loaded that are of that class will be processed by the assigned | |
42 | + * processor. | |
43 | + * | |
44 | + * @author Kirill Vainer | |
45 | + */ | |
46 | +public interface AssetProcessor { | |
47 | + /** | |
48 | + * Applies post processing to an asset. | |
49 | + * The method may return an object that is not the same | |
50 | + * instance as the parameter object, and it could be from a different class. | |
51 | + * | |
52 | + * @param obj The asset that was loaded from an {@link AssetLoader}. | |
53 | + * @return Either the same object with processing applied, or an instance | |
54 | + * of a new object. | |
55 | + */ | |
56 | + public Object postProcess(AssetKey key, Object obj); | |
57 | + | |
58 | + /** | |
59 | + * Creates a clone of the given asset. | |
60 | + * If no clone is desired, then the same instance can be returned, | |
61 | + * otherwise, a clone should be created. | |
62 | + * For example, a clone of a {@link Material} should have its own set | |
63 | + * of unique parameters that can be changed just for that instance, | |
64 | + * but it may share certain other data if it sees fit (like the {@link Shader}). | |
65 | + * | |
66 | + * @param obj The asset to clone | |
67 | + * @return The cloned asset, or the same as the given argument if no | |
68 | + * clone is needed. | |
69 | + */ | |
70 | + public Object createClone(Object obj); | |
71 | +} |
@@ -0,0 +1,86 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Redistribution and use in source and binary forms, with or without | |
6 | + * modification, are permitted provided that the following conditions are | |
7 | + * met: | |
8 | + * | |
9 | + * * Redistributions of source code must retain the above copyright | |
10 | + * notice, this list of conditions and the following disclaimer. | |
11 | + * | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * | |
16 | + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | |
17 | + * may be used to endorse or promote products derived from this software | |
18 | + * without specific prior written permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
22 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
27 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
28 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
29 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + */ | |
32 | +package com.jme3.asset; | |
33 | + | |
34 | +import com.jme3.asset.cache.WeakRefCloneAssetCache; | |
35 | + | |
36 | +/** | |
37 | + * Implementing the <code>CloneableSmartAsset</code> interface allows use | |
38 | + * of cloneable smart asset management. | |
39 | + * <p> | |
40 | + * Smart asset management requires cooperation from the {@link AssetKey}. | |
41 | + * In particular, the AssetKey should return {@link WeakRefCloneAssetCache} in its | |
42 | + * {@link AssetKey#getCacheType()} method. Also smart assets MUST | |
43 | + * create a clone of the asset and cannot return the same reference, | |
44 | + * e.g. {@link AssetProcessor#createClone(java.lang.Object) createClone(someAsset)} <code>!= someAsset</code>. | |
45 | + * <p> | |
46 | + * If the {@link AssetManager#loadAsset(com.jme3.asset.AssetKey) } method | |
47 | + * is called twice with the same asset key (equals() wise, not necessarily reference wise) | |
48 | + * then both assets will have the same asset key set (reference wise) via | |
49 | + * {@link AssetKey#AssetKey() }, then this asset key | |
50 | + * is used to track all instances of that asset. Once all clones of the asset | |
51 | + * are garbage collected, the shared asset key becomes unreachable and at that | |
52 | + * point it is removed from the smart asset cache. | |
53 | + */ | |
54 | +public interface CloneableSmartAsset extends Cloneable { | |
55 | + | |
56 | + /** | |
57 | + * Creates a clone of the asset. | |
58 | + * | |
59 | + * Please see {@link Object#clone() } for more info on how this method | |
60 | + * should be implemented. | |
61 | + * | |
62 | + * @return A clone of this asset. | |
63 | + * The cloned asset cannot reference equal this asset. | |
64 | + */ | |
65 | + public Object clone(); | |
66 | + | |
67 | + /** | |
68 | + * Set by the {@link AssetManager} to track this asset. | |
69 | + * | |
70 | + * Only clones of the asset has this set, the original copy that | |
71 | + * was loaded has this key set to null so that only the clones are tracked | |
72 | + * for garbage collection. | |
73 | + * | |
74 | + * @param key The AssetKey to set | |
75 | + */ | |
76 | + public void setKey(AssetKey key); | |
77 | + | |
78 | + /** | |
79 | + * Returns the asset key that is used to track this asset for garbage | |
80 | + * collection. | |
81 | + * | |
82 | + * @return the asset key that is used to track this asset for garbage | |
83 | + * collection. | |
84 | + */ | |
85 | + public AssetKey getKey(); | |
86 | +} |
@@ -0,0 +1,121 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Redistribution and use in source and binary forms, with or without | |
6 | + * modification, are permitted provided that the following conditions are | |
7 | + * met: | |
8 | + * | |
9 | + * * Redistributions of source code must retain the above copyright | |
10 | + * notice, this list of conditions and the following disclaimer. | |
11 | + * | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * | |
16 | + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | |
17 | + * may be used to endorse or promote products derived from this software | |
18 | + * without specific prior written permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
22 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
27 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
28 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
29 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + */ | |
32 | +package com.jme3.asset.cache; | |
33 | + | |
34 | +import com.jme3.asset.AssetKey; | |
35 | + | |
36 | +/** | |
37 | + * <code>AssetCache</code> is an interface for asset caches. | |
38 | + * Allowing storage of loaded resources in order to improve their access time | |
39 | + * if they are requested again in a short period of time. | |
40 | + * Depending on the asset type and how it is used, a specialized | |
41 | + * caching method can be selected that is most appropriate for that asset type. | |
42 | + * The asset cache must be thread safe. | |
43 | + * <p> | |
44 | + * Some caches are used to manage cloneable assets, which track reachability | |
45 | + * based on a shared key in all instances exposed in user code. | |
46 | + * E.g. {@link WeakRefCloneAssetCache} uses this approach. | |
47 | + * For those particular caches, either {@link #registerAssetClone(com.jme3.asset.AssetKey, java.lang.Object) } | |
48 | + * or {@link #notifyNoAssetClone() } <b>MUST</b> be called to avoid memory | |
49 | + * leaking following a successful {@link #addToCache(com.jme3.asset.AssetKey, java.lang.Object) } | |
50 | + * or {@link #getFromCache(com.jme3.asset.AssetKey) } call! | |
51 | + * | |
52 | + * @author Kirill Vainer | |
53 | + */ | |
54 | +public interface AssetCache { | |
55 | + /** | |
56 | + * Adds an asset to the cache. | |
57 | + * Once added, it should be possible to retrieve the asset | |
58 | + * by using the {@link #getFromCache(com.jme3.asset.AssetKey) } method. | |
59 | + * However the caching criteria may at some point choose that the asset | |
60 | + * should be removed from the cache to save memory, in that case, | |
61 | + * {@link #getFromCache(com.jme3.asset.AssetKey) } will return null. | |
62 | + * <p><font color="red">Thread-Safe</font> | |
63 | + * | |
64 | + * @param <T> The type of the asset to cache. | |
65 | + * @param key The asset key that can be used to look up the asset. | |
66 | + * @param obj The asset data to cache. | |
67 | + */ | |
68 | + public <T> void addToCache(AssetKey<T> key, T obj); | |
69 | + | |
70 | + /** | |
71 | + * This should be called by the asset manager when it has successfully | |
72 | + * acquired a cached asset (with {@link #getFromCache(com.jme3.asset.AssetKey) }) | |
73 | + * and cloned it for use. | |
74 | + * <p><font color="red">Thread-Safe</font> | |
75 | + * | |
76 | + * @param <T> The type of the asset to register. | |
77 | + * @param key The asset key of the loaded asset (used to retrieve from cache) | |
78 | + * @param clone The <strong>clone</strong> of the asset retrieved from | |
79 | + * the cache. | |
80 | + */ | |
81 | + public <T> void registerAssetClone(AssetKey<T> key, T clone); | |
82 | + | |
83 | + /** | |
84 | + * Notifies the cache that even though the methods {@link #addToCache(com.jme3.asset.AssetKey, java.lang.Object) } | |
85 | + * or {@link #getFromCache(com.jme3.asset.AssetKey) } were used, there won't | |
86 | + * be a call to {@link #registerAssetClone(com.jme3.asset.AssetKey, java.lang.Object) } | |
87 | + * for some reason. For example, if an error occurred during loading | |
88 | + * or if the addToCache/getFromCache were used from user code. | |
89 | + */ | |
90 | + public void notifyNoAssetClone(); | |
91 | + | |
92 | + /** | |
93 | + * Retrieves an asset from the cache. | |
94 | + * It is possible to add an asset to the cache using | |
95 | + * {@link #addToCache(com.jme3.asset.AssetKey, java.lang.Object) }. | |
96 | + * The asset may be removed from the cache automatically even if | |
97 | + * it was added previously, in that case, this method will return null. | |
98 | + * <p><font color="red">Thread-Safe</font> | |
99 | + * | |
100 | + * @param <T> The type of the asset to retrieve | |
101 | + * @param key The key used to lookup the asset. | |
102 | + * @return The asset that was previously cached, or null if not found. | |
103 | + */ | |
104 | + public <T> T getFromCache(AssetKey<T> key); | |
105 | + | |
106 | + /** | |
107 | + * Deletes an asset from the cache. | |
108 | + * <p><font color="red">Thread-Safe</font> | |
109 | + * | |
110 | + * @param key The asset key to find the asset to delete. | |
111 | + * @return True if the asset was successfully found in the cache | |
112 | + * and removed. | |
113 | + */ | |
114 | + public boolean deleteFromCache(AssetKey key); | |
115 | + | |
116 | + /** | |
117 | + * Deletes all assets from the cache. | |
118 | + * <p><font color="red">Thread-Safe</font> | |
119 | + */ | |
120 | + public void clearCache(); | |
121 | +} |
@@ -0,0 +1,71 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Redistribution and use in source and binary forms, with or without | |
6 | + * modification, are permitted provided that the following conditions are | |
7 | + * met: | |
8 | + * | |
9 | + * * Redistributions of source code must retain the above copyright | |
10 | + * notice, this list of conditions and the following disclaimer. | |
11 | + * | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * | |
16 | + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | |
17 | + * may be used to endorse or promote products derived from this software | |
18 | + * without specific prior written permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
22 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
27 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
28 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
29 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + */ | |
32 | +package com.jme3.asset.cache; | |
33 | + | |
34 | +import com.jme3.asset.AssetKey; | |
35 | +import java.util.concurrent.ConcurrentHashMap; | |
36 | + | |
37 | +/** | |
38 | + * <code>SimpleAssetCache</code> is an asset cache | |
39 | + * that caches assets without any automatic removal policy. The user | |
40 | + * is expected to manually call {@link #deleteFromCache(com.jme3.asset.AssetKey) } | |
41 | + * to delete any assets. | |
42 | + * | |
43 | + * @author Kirill Vainer | |
44 | + */ | |
45 | +public class SimpleAssetCache implements AssetCache { | |
46 | + | |
47 | + private final ConcurrentHashMap<AssetKey, Object> keyToAssetMap = new ConcurrentHashMap<AssetKey, Object>(); | |
48 | + | |
49 | + public <T> void addToCache(AssetKey<T> key, T obj) { | |
50 | + keyToAssetMap.put(key, obj); | |
51 | + } | |
52 | + | |
53 | + public <T> void registerAssetClone(AssetKey<T> key, T clone) { | |
54 | + } | |
55 | + | |
56 | + public <T> T getFromCache(AssetKey<T> key) { | |
57 | + return (T) keyToAssetMap.get(key); | |
58 | + } | |
59 | + | |
60 | + public boolean deleteFromCache(AssetKey key) { | |
61 | + return keyToAssetMap.remove(key) != null; | |
62 | + } | |
63 | + | |
64 | + public void clearCache() { | |
65 | + keyToAssetMap.clear(); | |
66 | + } | |
67 | + | |
68 | + public void notifyNoAssetClone() { | |
69 | + } | |
70 | + | |
71 | +} |
@@ -0,0 +1,123 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Redistribution and use in source and binary forms, with or without | |
6 | + * modification, are permitted provided that the following conditions are | |
7 | + * met: | |
8 | + * | |
9 | + * * Redistributions of source code must retain the above copyright | |
10 | + * notice, this list of conditions and the following disclaimer. | |
11 | + * | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * | |
16 | + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | |
17 | + * may be used to endorse or promote products derived from this software | |
18 | + * without specific prior written permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
22 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
27 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
28 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
29 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + */ | |
32 | +package com.jme3.asset.cache; | |
33 | + | |
34 | +import com.jme3.asset.AssetKey; | |
35 | +import com.jme3.asset.AssetProcessor; | |
36 | +import java.lang.ref.ReferenceQueue; | |
37 | +import java.lang.ref.WeakReference; | |
38 | +import java.util.concurrent.ConcurrentHashMap; | |
39 | +import java.util.logging.Level; | |
40 | +import java.util.logging.Logger; | |
41 | + | |
42 | +/** | |
43 | + * A garbage collector bound asset cache that handles non-clonable objects. | |
44 | + * This cache assumes that the asset given to the user is the same asset | |
45 | + * that has been stored in the cache, in other words, | |
46 | + * {@link AssetProcessor#createClone(java.lang.Object) } for that asset | |
47 | + * returns the same object as the argument. | |
48 | + * This implementation will remove the asset from the cache | |
49 | + * once the asset is no longer referenced in user code and memory is low, | |
50 | + * e.g. the VM feels like purging the weak references for that asset. | |
51 | + * | |
52 | + * @author Kirill Vainer | |
53 | + */ | |
54 | +public class WeakRefAssetCache implements AssetCache { | |
55 | + | |
56 | + private static final Logger logger = Logger.getLogger(WeakRefAssetCache.class.getName()); | |
57 | + | |
58 | + private final ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>(); | |
59 | + | |
60 | + private final ConcurrentHashMap<AssetKey, AssetRef> assetCache | |
61 | + = new ConcurrentHashMap<AssetKey, AssetRef>(); | |
62 | + | |
63 | + private static class AssetRef extends WeakReference<Object> { | |
64 | + | |
65 | + private final AssetKey assetKey; | |
66 | + | |
67 | + public AssetRef(AssetKey assetKey, Object originalAsset, ReferenceQueue<Object> refQueue){ | |
68 | + super(originalAsset, refQueue); | |
69 | + this.assetKey = assetKey; | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + private void removeCollectedAssets(){ | |
74 | + int removedAssets = 0; | |
75 | + for (AssetRef ref; (ref = (AssetRef)refQueue.poll()) != null;){ | |
76 | + // Asset was collected, note that at this point the asset cache | |
77 | + // might not even have this asset anymore, it is OK. | |
78 | + if (assetCache.remove(ref.assetKey) != null){ | |
79 | + removedAssets ++; | |
80 | + //System.out.println("WeakRefAssetCache: The asset " + ref.assetKey + " was purged from the cache"); | |
81 | + } | |
82 | + } | |
83 | + if (removedAssets >= 1) { | |
84 | + logger.log(Level.FINE, "WeakRefAssetCache: {0} assets were purged from the cache.", removedAssets); | |
85 | + } | |
86 | + } | |
87 | + | |
88 | + public <T> void addToCache(AssetKey<T> key, T obj) { | |
89 | + removeCollectedAssets(); | |
90 | + | |
91 | + // NOTE: Some thread issues can hapen if another | |
92 | + // thread is loading an asset with the same key .. | |
93 | + AssetRef ref = new AssetRef(key, obj, refQueue); | |
94 | + assetCache.put(key, ref); | |
95 | + | |
96 | +// Texture t = (Texture) obj; | |
97 | +// Image i = t.getImage(); | |
98 | +// System.out.println("add to cache " + System.identityHashCode(i)); | |
99 | + } | |
100 | + | |
101 | + public <T> T getFromCache(AssetKey<T> key) { | |
102 | + AssetRef ref = assetCache.get(key); | |
103 | + if (ref != null){ | |
104 | + return (T) ref.get(); | |
105 | + }else{ | |
106 | + return null; | |
107 | + } | |
108 | + } | |
109 | + | |
110 | + public boolean deleteFromCache(AssetKey key) { | |
111 | + return assetCache.remove(key) != null; | |
112 | + } | |
113 | + | |
114 | + public void clearCache() { | |
115 | + assetCache.clear(); | |
116 | + } | |
117 | + | |
118 | + public <T> void registerAssetClone(AssetKey<T> key, T clone) { | |
119 | + } | |
120 | + | |
121 | + public void notifyNoAssetClone() { | |
122 | + } | |
123 | +} |
@@ -0,0 +1,206 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Redistribution and use in source and binary forms, with or without | |
6 | + * modification, are permitted provided that the following conditions are | |
7 | + * met: | |
8 | + * | |
9 | + * * Redistributions of source code must retain the above copyright | |
10 | + * notice, this list of conditions and the following disclaimer. | |
11 | + * | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * | |
16 | + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors | |
17 | + * may be used to endorse or promote products derived from this software | |
18 | + * without specific prior written permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
22 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
23 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
27 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
28 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
29 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + */ | |
32 | +package com.jme3.asset.cache; | |
33 | + | |
34 | +import com.jme3.asset.AssetKey; | |
35 | +import com.jme3.asset.CloneableSmartAsset; | |
36 | +import java.lang.ref.PhantomReference; | |
37 | +import java.lang.ref.ReferenceQueue; | |
38 | +import java.lang.ref.WeakReference; | |
39 | +import java.util.ArrayList; | |
40 | +import java.util.concurrent.ConcurrentHashMap; | |
41 | +import java.util.logging.Level; | |
42 | +import java.util.logging.Logger; | |
43 | + | |
44 | +/** | |
45 | + * <codeWeakRefCloneAssetCache</code> caches cloneable assets in a weak-key | |
46 | + * cache, allowing them to be collected when memory is low. | |
47 | + * The cache stores weak references to the asset keys, so that | |
48 | + * when all clones of the original asset are collected, will cause the | |
49 | + * asset to be automatically removed from the cache. | |
50 | + * | |
51 | +* @author Kirill Vainer | |
52 | + */ | |
53 | +public class WeakRefCloneAssetCache implements AssetCache { | |
54 | + | |
55 | + private static final Logger logger = Logger.getLogger(WeakRefAssetCache.class.getName()); | |
56 | + | |
57 | + private final ReferenceQueue<AssetKey> refQueue = new ReferenceQueue<AssetKey>(); | |
58 | + | |
59 | + /** | |
60 | + * Maps cloned key to AssetRef which has a weak ref to the original | |
61 | + * key and a strong ref to the original asset. | |
62 | + */ | |
63 | + private final ConcurrentHashMap<AssetKey, AssetRef> smartCache | |
64 | + = new ConcurrentHashMap<AssetKey, AssetRef>(); | |
65 | + | |
66 | + /** | |
67 | + * Stored in the ReferenceQueue to find out when originalKey is collected | |
68 | + * by GC. Once collected, the clonedKey is used to remove the asset | |
69 | + * from the cache. | |
70 | + */ | |
71 | + private static final class KeyRef extends PhantomReference<AssetKey> { | |
72 | + | |
73 | + AssetKey clonedKey; | |
74 | + | |
75 | + public KeyRef(AssetKey originalKey, ReferenceQueue<AssetKey> refQueue) { | |
76 | + super(originalKey, refQueue); | |
77 | + clonedKey = originalKey.clone(); | |
78 | + } | |
79 | + } | |
80 | + | |
81 | + /** | |
82 | + * Stores the original key and original asset. | |
83 | + * The asset info contains a cloneable asset (e.g. the original, from | |
84 | + * which all clones are made). Also a weak reference to the | |
85 | + * original key which is used when the clones are produced. | |
86 | + */ | |
87 | + private static final class AssetRef extends WeakReference<AssetKey> { | |
88 | + | |
89 | + CloneableSmartAsset asset; | |
90 | + | |
91 | + public AssetRef(CloneableSmartAsset originalAsset, AssetKey originalKey) { | |
92 | + super(originalKey); | |
93 | + this.asset = originalAsset; | |
94 | + } | |
95 | + } | |
96 | + | |
97 | + private final ThreadLocal<ArrayList<AssetKey>> assetLoadStack | |
98 | + = new ThreadLocal<ArrayList<AssetKey>>() { | |
99 | + @Override | |
100 | + protected ArrayList<AssetKey> initialValue() { | |
101 | + return new ArrayList<AssetKey>(); | |
102 | + } | |
103 | + }; | |
104 | + | |
105 | + private void removeCollectedAssets(){ | |
106 | + int removedAssets = 0; | |
107 | + for (KeyRef ref; (ref = (KeyRef)refQueue.poll()) != null;){ | |
108 | + // (Cannot use ref.get() since it was just collected by GC!) | |
109 | + AssetKey key = ref.clonedKey; | |
110 | + | |
111 | + // Asset was collected, note that at this point the asset cache | |
112 | + // might not even have this asset anymore, it is OK. | |
113 | + if (smartCache.remove(key) != null){ | |
114 | + removedAssets ++; | |
115 | + //System.out.println("WeakRefAssetCache: The asset " + ref.assetKey + " was purged from the cache"); | |
116 | + } | |
117 | + } | |
118 | + if (removedAssets >= 1) { | |
119 | + logger.log(Level.FINE, "WeakRefAssetCache: {0} assets were purged from the cache.", removedAssets); | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + public <T> void addToCache(AssetKey<T> originalKey, T obj) { | |
124 | + // Make room for new asset | |
125 | + removeCollectedAssets(); | |
126 | + | |
127 | + CloneableSmartAsset asset = (CloneableSmartAsset) obj; | |
128 | + | |
129 | + // No circular references, since the original asset is | |
130 | + // strongly referenced, we don't want the key strongly referenced. | |
131 | + asset.setKey(null); | |
132 | + | |
133 | + // Start tracking the collection of originalKey | |
134 | + // (this adds the KeyRef to the ReferenceQueue) | |
135 | + KeyRef ref = new KeyRef(originalKey, refQueue); | |
136 | + | |
137 | + // Place the asset in the cache, but use a clone of | |
138 | + // the original key. | |
139 | + smartCache.put(ref.clonedKey, new AssetRef(asset, originalKey)); | |
140 | + | |
141 | + // Push the original key used to load the asset | |
142 | + // so that it can be set on the clone later | |
143 | + ArrayList<AssetKey> loadStack = assetLoadStack.get(); | |
144 | + loadStack.add(originalKey); | |
145 | + } | |
146 | + | |
147 | + public <T> void registerAssetClone(AssetKey<T> key, T clone) { | |
148 | + ArrayList<AssetKey> loadStack = assetLoadStack.get(); | |
149 | + ((CloneableSmartAsset)clone).setKey(loadStack.remove(loadStack.size() - 1)); | |
150 | + } | |
151 | + | |
152 | + public void notifyNoAssetClone() { | |
153 | + ArrayList<AssetKey> loadStack = assetLoadStack.get(); | |
154 | + loadStack.remove(loadStack.size() - 1); | |
155 | + } | |
156 | + | |
157 | + public <T> T getFromCache(AssetKey<T> key) { | |
158 | + AssetRef smartInfo; | |
159 | + synchronized (smartCache){ | |
160 | + smartInfo = smartCache.get(key); | |
161 | + } | |
162 | + | |
163 | + if (smartInfo == null) { | |
164 | + return null; | |
165 | + } else { | |
166 | + // NOTE: Optimization so that registerAssetClone() | |
167 | + // can check this and determine that the asset clone | |
168 | + // belongs to the asset retrieved here. | |
169 | + AssetKey keyForTheClone = smartInfo.get(); | |
170 | + if (keyForTheClone == null){ | |
171 | + // The asset was JUST collected by GC | |
172 | + // (between here and smartCache.get) | |
173 | + return null; | |
174 | + } | |
175 | + | |
176 | + // Prevent original key from getting collected | |
177 | + // while an asset is loaded for it. | |
178 | + ArrayList<AssetKey> loadStack = assetLoadStack.get(); | |
179 | + loadStack.add(keyForTheClone); | |
180 | + | |
181 | + return (T) smartInfo.asset; | |
182 | + } | |
183 | + } | |
184 | + | |
185 | + public boolean deleteFromCache(AssetKey key) { | |
186 | + ArrayList<AssetKey> loadStack = assetLoadStack.get(); | |
187 | + | |
188 | + if (!loadStack.isEmpty()){ | |
189 | + throw new UnsupportedOperationException("Cache cannot be modified" | |
190 | + + "while assets are being loaded"); | |
191 | + } | |
192 | + | |
193 | + return smartCache.remove(key) != null; | |
194 | + } | |
195 | + | |
196 | + public void clearCache() { | |
197 | + ArrayList<AssetKey> loadStack = assetLoadStack.get(); | |
198 | + | |
199 | + if (!loadStack.isEmpty()){ | |
200 | + throw new UnsupportedOperationException("Cache cannot be modified" | |
201 | + + "while assets are being loaded"); | |
202 | + } | |
203 | + | |
204 | + smartCache.clear(); | |
205 | + } | |
206 | +} |