* AssetCache is now an interface and can be extended by user. Moved to com.jme3.asset.cache package.
* Added 3 implementations of AssetCache: SimpleAssetCache, WeakRefAssetCache and WeakRefCloneAssetCache * Added AssetProcessor interface that handles cloning and post processing of assets after they are loaded * AssetKey can now configure which cache/processor to use for a particular asset type * Added AssetManager unregisterLoader method * AssetManager now supports more than one AssetLoadListener * Javadoc improvements in AssetManager * Asset interface now renamed to CloneableSmartAsset (which more accurately describes its behavior and use case) * DesktopAssetManager now makes proper use of the new AssetProcessor/AssetCache classes when handling asset loading * Added proper equals/hashCode methods to many AssetKey subclasses, which is required for the new system to work properly * All AssetKeys were rewritten to work with the new asset system * loadAsset(AudioKey) now returns an AudioNode and not AudioData, similar to the behavior of loadAsset(TextureKey) returning a Texture and not an Image. Because of that, the key storage in AudioData has been removed. * Texture, Spatial, and Material are all cloneable smart assets now and will be cleared from the cache when all instances become unreachable * Improved the existing TestAssetCache test to make sure the new system works git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9309 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
0c37fc46e9
commit
bd4214f3bd
@ -36,6 +36,7 @@ import com.jme3.asset.AssetInfo;
|
|||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -78,12 +79,15 @@ public class GLSLLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class GlslDependKey extends AssetKey<InputStream> {
|
private class GlslDependKey extends AssetKey<InputStream> {
|
||||||
|
|
||||||
public GlslDependKey(String name) {
|
public GlslDependKey(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldCache(){
|
public Class<? extends AssetCache> getCacheType() {
|
||||||
return false;
|
// Disallow caching here
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jme3.asset;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An <code>AssetCache</code> allows storage of loaded resources in order
|
|
||||||
* to improve their access time if they are requested again in a short period
|
|
||||||
* of time. The AssetCache stores weak references to the resources, allowing
|
|
||||||
* Java's garbage collector to request deletion of rarely used resources
|
|
||||||
* when heap memory is low.
|
|
||||||
*/
|
|
||||||
public class AssetCache {
|
|
||||||
|
|
||||||
public static final class SmartAssetInfo {
|
|
||||||
public WeakReference<AssetKey> smartKey;
|
|
||||||
public Asset asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final WeakHashMap<AssetKey, SmartAssetInfo> smartCache
|
|
||||||
= new WeakHashMap<AssetKey, SmartAssetInfo>();
|
|
||||||
private final HashMap<AssetKey, Object> regularCache = new HashMap<AssetKey, Object>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a resource to the cache.
|
|
||||||
* <br/><br/>
|
|
||||||
* <font color="red">Thread-safe.</font>
|
|
||||||
* @see #getFromCache(java.lang.String)
|
|
||||||
*/
|
|
||||||
public void addToCache(AssetKey key, Object obj){
|
|
||||||
synchronized (regularCache){
|
|
||||||
if (obj instanceof Asset && key.useSmartCache()){
|
|
||||||
// put in smart cache
|
|
||||||
Asset asset = (Asset) obj;
|
|
||||||
asset.setKey(null); // no circular references
|
|
||||||
SmartAssetInfo smartInfo = new SmartAssetInfo();
|
|
||||||
smartInfo.asset = asset;
|
|
||||||
// use the original key as smart key
|
|
||||||
smartInfo.smartKey = new WeakReference<AssetKey>(key);
|
|
||||||
smartCache.put(key, smartInfo);
|
|
||||||
}else{
|
|
||||||
// put in regular cache
|
|
||||||
regularCache.put(key, obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an asset from the cache, returns true if it was deleted successfuly.
|
|
||||||
* <br/><br/>
|
|
||||||
* <font color="red">Thread-safe.</font>
|
|
||||||
*/
|
|
||||||
public boolean deleteFromCache(AssetKey key){
|
|
||||||
synchronized (regularCache){
|
|
||||||
if (key.useSmartCache()){
|
|
||||||
return smartCache.remove(key) != null;
|
|
||||||
}else{
|
|
||||||
return regularCache.remove(key) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an object from the cache given an asset key.
|
|
||||||
* <br/><br/>
|
|
||||||
* <font color="red">Thread-safe.</font>
|
|
||||||
* @param key
|
|
||||||
* @return the object matching the {@link AssetKey}
|
|
||||||
*/
|
|
||||||
public Object getFromCache(AssetKey key){
|
|
||||||
synchronized (regularCache){
|
|
||||||
if (key.useSmartCache()) {
|
|
||||||
return smartCache.get(key).asset;
|
|
||||||
} else {
|
|
||||||
return regularCache.get(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves smart asset info from the cache.
|
|
||||||
* @param key
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public SmartAssetInfo getFromSmartCache(AssetKey key){
|
|
||||||
return smartCache.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes all the assets in the regular cache.
|
|
||||||
*/
|
|
||||||
public void deleteAllAssets(){
|
|
||||||
synchronized (regularCache){
|
|
||||||
regularCache.clear();
|
|
||||||
smartCache.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
import com.jme3.asset.cache.SimpleAssetCache;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -64,20 +66,25 @@ public class AssetKey<T> implements Savable {
|
|||||||
idx = name.lastIndexOf('.');
|
idx = name.lastIndexOf('.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idx <= 0 || idx == name.length() - 1)
|
if (idx <= 0 || idx == name.length() - 1) {
|
||||||
return "";
|
return "";
|
||||||
else
|
} else {
|
||||||
return name.substring(idx + 1).toLowerCase();
|
return name.substring(idx + 1).toLowerCase();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static String getFolder(String name) {
|
protected static String getFolder(String name) {
|
||||||
int idx = name.lastIndexOf('/');
|
int idx = name.lastIndexOf('/');
|
||||||
if (idx <= 0 || idx == name.length() - 1)
|
if (idx <= 0 || idx == name.length() - 1) {
|
||||||
return "";
|
return "";
|
||||||
else
|
} else {
|
||||||
return name.substring(0, idx + 1);
|
return name.substring(0, idx + 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The asset path
|
||||||
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -90,6 +97,11 @@ public class AssetKey<T> implements Savable {
|
|||||||
return extension;
|
return extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The folder in which the asset is located in.
|
||||||
|
* E.g. if the {@link #getName() name} is "Models/MyModel/MyModel.j3o"
|
||||||
|
* then "Models/MyModel/" is returned.
|
||||||
|
*/
|
||||||
public String getFolder(){
|
public String getFolder(){
|
||||||
if (folder == null)
|
if (folder == null)
|
||||||
folder = getFolder(name);
|
folder = getFolder(name);
|
||||||
@ -98,41 +110,20 @@ public class AssetKey<T> implements Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do any post-processing on the resource after it has been loaded.
|
* @return The preferred cache class for this asset type. Specify "null"
|
||||||
* @param asset
|
* if caching is to be disabled. By default the
|
||||||
|
* {@link SimpleAssetCache} is returned.
|
||||||
*/
|
*/
|
||||||
public Object postProcess(Object asset){
|
public Class<? extends AssetCache> getCacheType(){
|
||||||
return asset;
|
return SimpleAssetCache.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of the asset, based on a prototype that is stored
|
* @return The preferred processor type for this asset type. Specify "null"
|
||||||
* in the cache. Implementations are allowed to return the given parameter
|
* if no processing is required.
|
||||||
* as-is if it is considered that cloning is not necessary for that particular
|
|
||||||
* asset type.
|
|
||||||
*
|
|
||||||
* @param asset The asset to be cloned.
|
|
||||||
* @return The asset, possibly cloned.
|
|
||||||
*/
|
*/
|
||||||
public Object createClonedInstance(Object asset){
|
public Class<? extends AssetProcessor> getProcessorType(){
|
||||||
return asset;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if the asset for this key should be cached. Subclasses
|
|
||||||
* should override this method if they want to override caching behavior.
|
|
||||||
*/
|
|
||||||
public boolean shouldCache(){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Should return true, if the asset objects implement the "Asset"
|
|
||||||
* interface and want to be removed from the cache when no longer
|
|
||||||
* referenced in user-code.
|
|
||||||
*/
|
|
||||||
public boolean useSmartCache(){
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,66 +32,124 @@
|
|||||||
|
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
import com.jme3.audio.AudioData;
|
import com.jme3.asset.plugins.ClasspathLocator;
|
||||||
|
import com.jme3.asset.plugins.FileLocator;
|
||||||
import com.jme3.audio.AudioKey;
|
import com.jme3.audio.AudioKey;
|
||||||
|
import com.jme3.audio.AudioNode;
|
||||||
import com.jme3.font.BitmapFont;
|
import com.jme3.font.BitmapFont;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.plugins.OBJLoader;
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.ShaderKey;
|
import com.jme3.shader.ShaderKey;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
|
import com.jme3.texture.plugins.TGALoader;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>AssetManager</code> provides an interface for managing the data assets
|
* <code>AssetManager</code> provides an interface for managing the data assets
|
||||||
* of a jME3 application.
|
* of a jME3 application.
|
||||||
|
* <p>
|
||||||
|
* The asset manager provides a means to register {@link AssetLocator}s,
|
||||||
|
* which are used to find asset data on disk, network, or other file system.
|
||||||
|
* The asset locators are invoked in order of addition to find the asset data.
|
||||||
|
* Use the {@link #registerLocator(java.lang.String, java.lang.Class) } method
|
||||||
|
* to add new {@link AssetLocator}s.
|
||||||
|
* Some examples of locators:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link FileLocator} - Used to find assets on the local file system.</li>
|
||||||
|
* <li>{@link ClasspathLocator} - Used to find assets in the Java classpath</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* The asset data is represented by the {@link AssetInfo} class, this
|
||||||
|
* data is passed into the registered {@link AssetLoader}s in order to
|
||||||
|
* convert the data into a usable object. Use the
|
||||||
|
* {@link #registerLoader(java.lang.Class, java.lang.String[]) } method
|
||||||
|
* to add loaders.
|
||||||
|
* Some examples of loaders:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link OBJLoader} - Used to load Wavefront .OBJ model files</li>
|
||||||
|
* <li>{@link TGALoader} - Used to load Targa image files</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Once the asset has been loaded,
|
||||||
*/
|
*/
|
||||||
public interface AssetManager {
|
public interface AssetManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a ClassLoader that is used to load *Classes* that are needed for Assets like j3o models.
|
* Adds a {@link ClassLoader} that is used to load {@link Class classes}
|
||||||
* This does *not* allow loading assets from that classpath, use registerLocator for that.
|
* that are needed for finding and loading Assets.
|
||||||
* @param loader A ClassLoader that Classes in asset files can be loaded from
|
* This does <strong>not</strong> allow loading assets from that classpath,
|
||||||
|
* use registerLocator for that.
|
||||||
|
*
|
||||||
|
* @param loader A ClassLoader that Classes in asset files can be loaded from.
|
||||||
*/
|
*/
|
||||||
public void addClassLoader(ClassLoader loader);
|
public void addClassLoader(ClassLoader loader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a ClassLoader from the list of registered ClassLoaders
|
* Remove a {@link ClassLoader} from the list of registered ClassLoaders
|
||||||
*/
|
*/
|
||||||
public void removeClassLoader(ClassLoader loader);
|
public void removeClassLoader(ClassLoader loader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the list of registered ClassLoaders that are used for loading Classes from
|
* Retrieve the list of registered ClassLoaders that are used for loading
|
||||||
* asset files.
|
* {@link Class classes} from asset files.
|
||||||
*/
|
*/
|
||||||
public List<ClassLoader> getClassLoaders();
|
public List<ClassLoader> getClassLoaders();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a loader for the given extensions.
|
* Registers a loader for the given extensions.
|
||||||
|
*
|
||||||
* @param loaderClassName
|
* @param loaderClassName
|
||||||
* @param extensions
|
* @param extensions
|
||||||
|
*
|
||||||
|
* @deprecated Please use {@link #registerLoader(java.lang.Class, java.lang.String[]) }
|
||||||
|
* together with {@link Class#forName(java.lang.String) } to find a class
|
||||||
|
* and then register it.
|
||||||
|
*
|
||||||
|
* @deprecated Please use {@link #registerLoader(java.lang.Class, java.lang.String[]) }
|
||||||
|
* with {@link Class#forName(java.lang.String) } instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void registerLoader(String loaderClassName, String ... extensions);
|
public void registerLoader(String loaderClassName, String ... extensions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an {@link AssetLocator} by using a class name, instead of
|
* Registers an {@link AssetLocator} by using a class name.
|
||||||
* a class instance. See the {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
|
* See the {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
|
||||||
* method for more information.
|
* method for more information.
|
||||||
*
|
*
|
||||||
* @param rootPath The root path from which to locate assets, implementation
|
* @param rootPath The root path from which to locate assets, this
|
||||||
* dependent.
|
* depends on the implementation of the asset locator.
|
||||||
|
* A URL based locator will expect a url folder such as "http://www.example.com/"
|
||||||
|
* while a File based locator will expect a file path (OS dependent).
|
||||||
* @param locatorClassName The full class name of the {@link AssetLocator}
|
* @param locatorClassName The full class name of the {@link AssetLocator}
|
||||||
* implementation.
|
* implementation.
|
||||||
|
*
|
||||||
|
* @deprecated Please use {@link #registerLocator(java.lang.String, java.lang.Class) }
|
||||||
|
* together with {@link Class#forName(java.lang.String) } to find a class
|
||||||
|
* and then register it.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void registerLocator(String rootPath, String locatorClassName);
|
public void registerLocator(String rootPath, String locatorClassName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Register an {@link AssetLoader} by using a class object.
|
||||||
*
|
*
|
||||||
* @param loaderClass
|
* @param loaderClass
|
||||||
* @param extensions
|
* @param extensions
|
||||||
*/
|
*/
|
||||||
public void registerLoader(Class<? extends AssetLoader> loaderClass, String ... extensions);
|
public void registerLoader(Class<? extends AssetLoader> loaderClass, String ... extensions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a {@link AssetLoader} from loading its assigned extensions.
|
||||||
|
* This undoes the effect of calling
|
||||||
|
* {@link #registerLoader(java.lang.Class, java.lang.String[]) }.
|
||||||
|
*
|
||||||
|
* @param loaderClass The loader class to unregister.
|
||||||
|
* @see #registerLoader(java.lang.Class, java.lang.String[])
|
||||||
|
*/
|
||||||
|
public void unregisterLoader(Class<? extends AssetLoader> loaderClass);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the given locator class for locating assets with this
|
* Registers the given locator class for locating assets with this
|
||||||
* <code>AssetManager</code>. {@link AssetLocator}s are invoked in the order
|
* <code>AssetManager</code>. {@link AssetLocator}s are invoked in the order
|
||||||
@ -119,16 +177,42 @@ public interface AssetManager {
|
|||||||
* @param rootPath Should be the same as the root path specified in {@link
|
* @param rootPath Should be the same as the root path specified in {@link
|
||||||
* #registerLocator(java.lang.String, java.lang.Class) }.
|
* #registerLocator(java.lang.String, java.lang.Class) }.
|
||||||
* @param locatorClass The locator class to unregister
|
* @param locatorClass The locator class to unregister
|
||||||
|
*
|
||||||
|
* @see #registerLocator(java.lang.String, java.lang.Class)
|
||||||
*/
|
*/
|
||||||
public void unregisterLocator(String rootPath, Class<? extends AssetLocator> locatorClass);
|
public void unregisterLocator(String rootPath, Class<? extends AssetLocator> locatorClass);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an {@link AssetEventListener} to receive events from this
|
* Add an {@link AssetEventListener} to receive events from this
|
||||||
* <code>AssetManager</code>. There can only be one {@link AssetEventListener}
|
* <code>AssetManager</code>.
|
||||||
* associated with an <code>AssetManager</code>
|
* @param listener The asset event listener to add
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
*/
|
||||||
|
public void addAssetEventListener(AssetEventListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an {@link AssetEventListener} from receiving events from this
|
||||||
|
* <code>AssetManager</code>
|
||||||
|
* @param listener The asset event listener to remove
|
||||||
|
*/
|
||||||
|
public void removeAssetEventListener(AssetEventListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all asset event listeners.
|
||||||
|
*
|
||||||
|
* @see #addAssetEventListener(com.jme3.asset.AssetEventListener)
|
||||||
|
*/
|
||||||
|
public void clearAssetEventListeners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an {@link AssetEventListener} to receive events from this
|
||||||
|
* <code>AssetManager</code>. Any currently added listeners are
|
||||||
|
* cleared and then the given listener is added.
|
||||||
|
*
|
||||||
|
* @param listener The listener to set
|
||||||
|
* @deprecated Please use {@link #addAssetEventListener(com.jme3.asset.AssetEventListener) }
|
||||||
|
* to listen for asset events.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAssetEventListener(AssetEventListener listener);
|
public void setAssetEventListener(AssetEventListener listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +246,7 @@ public interface AssetManager {
|
|||||||
public <T> T loadAsset(AssetKey<T> key);
|
public <T> T loadAsset(AssetKey<T> key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a named asset by name, calling this method
|
* Load an asset by name, calling this method
|
||||||
* is the same as calling
|
* is the same as calling
|
||||||
* <code>
|
* <code>
|
||||||
* loadAsset(new AssetKey(name)).
|
* loadAsset(new AssetKey(name)).
|
||||||
@ -199,27 +283,27 @@ public interface AssetManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load audio file, supported types are WAV or OGG.
|
* Load audio file, supported types are WAV or OGG.
|
||||||
* @param key
|
* @param key Asset key of the audio file to load
|
||||||
* @return The audio data loaded
|
* @return The audio data loaded
|
||||||
*
|
*
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||||
*/
|
*/
|
||||||
public AudioData loadAudio(AudioKey key);
|
public AudioNode loadAudio(AudioKey key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load audio file, supported types are WAV or OGG.
|
* Load audio file, supported types are WAV or OGG.
|
||||||
* The file is loaded without stream-mode.
|
* The file is loaded without stream-mode.
|
||||||
* @param name
|
* @param name Asset name of the audio file to load
|
||||||
* @return The audio data loaded
|
* @return The audio data loaded
|
||||||
*
|
*
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||||
*/
|
*/
|
||||||
public AudioData loadAudio(String name);
|
public AudioNode loadAudio(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a named model. Models can be jME3 object files (J3O) or
|
* Loads a 3D model with a ModelKey.
|
||||||
* OgreXML/OBJ files.
|
* Models can be jME3 object files (J3O) or OgreXML/OBJ files.
|
||||||
* @param key
|
* @param key Asset key of the model to load
|
||||||
* @return The model that was loaded
|
* @return The model that was loaded
|
||||||
*
|
*
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||||
@ -227,9 +311,9 @@ public interface AssetManager {
|
|||||||
public Spatial loadModel(ModelKey key);
|
public Spatial loadModel(ModelKey key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a named model. Models can be jME3 object files (J3O) or
|
* Loads a 3D model. Models can be jME3 object files (J3O) or
|
||||||
* OgreXML/OBJ files.
|
* OgreXML/OBJ files.
|
||||||
* @param name
|
* @param name Asset name of the model to load
|
||||||
* @return The model that was loaded
|
* @return The model that was loaded
|
||||||
*
|
*
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||||
@ -237,8 +321,8 @@ public interface AssetManager {
|
|||||||
public Spatial loadModel(String name);
|
public Spatial loadModel(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a material (J3M) file.
|
* Load a material instance (J3M) file.
|
||||||
* @param name
|
* @param name Asset name of the material to load
|
||||||
* @return The material that was loaded
|
* @return The material that was loaded
|
||||||
*
|
*
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||||
@ -256,7 +340,7 @@ public interface AssetManager {
|
|||||||
* Load a font file. Font files are in AngelCode text format,
|
* Load a font file. Font files are in AngelCode text format,
|
||||||
* and are with the extension "fnt".
|
* and are with the extension "fnt".
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name Asset name of the font to load
|
||||||
* @return The font loaded
|
* @return The font loaded
|
||||||
*
|
*
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||||
|
72
engine/src/core/com/jme3/asset/AssetProcessor.java
Normal file
72
engine/src/core/com/jme3/asset/AssetProcessor.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AssetProcessor</code> is used to apply processing to assets
|
||||||
|
* after they have been loaded. They are assigned to a particular
|
||||||
|
* asset type (which is represented by a {@link Class} and any assets
|
||||||
|
* loaded that are of that class will be processed by the assigned
|
||||||
|
* processor.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public interface AssetProcessor {
|
||||||
|
/**
|
||||||
|
* Applies post processing to an asset.
|
||||||
|
* The method may return an object that is not the same
|
||||||
|
* instance as the parameter object, and it could be from a different class.
|
||||||
|
*
|
||||||
|
* @param obj The asset that was loaded from an {@link AssetLoader}.
|
||||||
|
* @return Either the same object with processing applied, or an instance
|
||||||
|
* of a new object.
|
||||||
|
*/
|
||||||
|
public Object postProcess(AssetKey key, Object obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a clone of the given asset.
|
||||||
|
* If no clone is desired, then the same instance can be returned,
|
||||||
|
* otherwise, a clone should be created.
|
||||||
|
* For example, a clone of a {@link Material} should have its own set
|
||||||
|
* of unique parameters that can be changed just for that instance,
|
||||||
|
* but it may share certain other data if it sees fit (like the {@link Shader}).
|
||||||
|
*
|
||||||
|
* @param obj The asset to clone
|
||||||
|
* @return The cloned asset, or the same as the given argument if no
|
||||||
|
* clone is needed.
|
||||||
|
*/
|
||||||
|
public Object createClone(Object obj);
|
||||||
|
}
|
52
engine/src/core/com/jme3/asset/CloneableAssetProcessor.java
Normal file
52
engine/src/core/com/jme3/asset/CloneableAssetProcessor.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>CloneableAssetProcessor</code> simply calls {@link Object#clone() }
|
||||||
|
* on assets to clone them. No processing is applied.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public class CloneableAssetProcessor implements AssetProcessor {
|
||||||
|
|
||||||
|
public Object postProcess(AssetKey key, Object obj) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createClone(Object obj) {
|
||||||
|
CloneableSmartAsset asset = (CloneableSmartAsset) obj;
|
||||||
|
return asset.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,14 +32,17 @@
|
|||||||
|
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.asset.cache.WeakRefCloneAssetCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementing the asset interface allows use of smart asset management.
|
* Implementing the <code>CloneableSmartAsset</code> interface allows use
|
||||||
|
* of cloneable smart asset management.
|
||||||
* <p>
|
* <p>
|
||||||
* Smart asset management requires cooperation from the {@link AssetKey}.
|
* Smart asset management requires cooperation from the {@link AssetKey}.
|
||||||
* In particular, the AssetKey should return true in its
|
* In particular, the AssetKey should return {@link WeakRefCloneAssetCache} in its
|
||||||
* {@link AssetKey#useSmartCache() } method. Also smart assets MUST
|
* {@link AssetKey#getCacheType()} method. Also smart assets MUST
|
||||||
* create a clone of the asset and cannot return the same reference,
|
* create a clone of the asset and cannot return the same reference,
|
||||||
* e.g. {@link AssetKey#createClonedInstance(java.lang.Object) createCloneInstance(someAsset)} <code>!= someAsset</code>.
|
* e.g. {@link AssetProcessor#createClone(java.lang.Object) createClone(someAsset)} <code>!= someAsset</code>.
|
||||||
* <p>
|
* <p>
|
||||||
* If the {@link AssetManager#loadAsset(com.jme3.asset.AssetKey) } method
|
* If the {@link AssetManager#loadAsset(com.jme3.asset.AssetKey) } method
|
||||||
* is called twice with the same asset key (equals() wise, not necessarily reference wise)
|
* is called twice with the same asset key (equals() wise, not necessarily reference wise)
|
||||||
@ -49,7 +52,18 @@ package com.jme3.asset;
|
|||||||
* are garbage collected, the shared asset key becomes unreachable and at that
|
* are garbage collected, the shared asset key becomes unreachable and at that
|
||||||
* point it is removed from the smart asset cache.
|
* point it is removed from the smart asset cache.
|
||||||
*/
|
*/
|
||||||
public interface Asset {
|
public interface CloneableSmartAsset extends Cloneable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a clone of the asset.
|
||||||
|
*
|
||||||
|
* Please see {@link Object#clone() } for more info on how this method
|
||||||
|
* should be implemented.
|
||||||
|
*
|
||||||
|
* @return A clone of this asset.
|
||||||
|
* The cloned asset cannot reference equal this asset.
|
||||||
|
*/
|
||||||
|
public Object clone();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set by the {@link AssetManager} to track this asset.
|
* Set by the {@link AssetManager} to track this asset.
|
@ -32,9 +32,10 @@
|
|||||||
|
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
import com.jme3.asset.AssetCache.SmartAssetInfo;
|
import com.jme3.asset.cache.AssetCache;
|
||||||
import com.jme3.audio.AudioData;
|
import com.jme3.asset.cache.SimpleAssetCache;
|
||||||
import com.jme3.audio.AudioKey;
|
import com.jme3.audio.AudioKey;
|
||||||
|
import com.jme3.audio.AudioNode;
|
||||||
import com.jme3.font.BitmapFont;
|
import com.jme3.font.BitmapFont;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
@ -48,6 +49,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -61,14 +63,13 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AssetManager.class.getName());
|
private static final Logger logger = Logger.getLogger(AssetManager.class.getName());
|
||||||
|
|
||||||
private final AssetCache cache = new AssetCache();
|
|
||||||
private final ImplHandler handler = new ImplHandler(this);
|
private final ImplHandler handler = new ImplHandler(this);
|
||||||
|
|
||||||
private AssetEventListener eventListener = null;
|
private CopyOnWriteArrayList<AssetEventListener> eventListeners =
|
||||||
private List<ClassLoader> classLoaders;
|
new CopyOnWriteArrayList<AssetEventListener>();
|
||||||
|
|
||||||
// private final ThreadingManager threadingMan = new ThreadingManager(this);
|
private List<ClassLoader> classLoaders =
|
||||||
// private final Set<AssetKey> alreadyLoadingSet = new HashSet<AssetKey>();
|
Collections.synchronizedList(new ArrayList<ClassLoader>());
|
||||||
|
|
||||||
public DesktopAssetManager(){
|
public DesktopAssetManager(){
|
||||||
this(null);
|
this(null);
|
||||||
@ -81,6 +82,12 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
|
|
||||||
public DesktopAssetManager(URL configFile){
|
public DesktopAssetManager(URL configFile){
|
||||||
if (configFile != null){
|
if (configFile != null){
|
||||||
|
loadConfigFile(configFile);
|
||||||
|
}
|
||||||
|
logger.info("DesktopAssetManager created.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConfigFile(URL configFile){
|
||||||
InputStream stream = null;
|
InputStream stream = null;
|
||||||
try{
|
try{
|
||||||
AssetConfig cfg = new AssetConfig(this);
|
AssetConfig cfg = new AssetConfig(this);
|
||||||
@ -96,29 +103,34 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info("DesktopAssetManager created.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addClassLoader(ClassLoader loader) {
|
public void addClassLoader(ClassLoader loader) {
|
||||||
if(classLoaders == null)
|
|
||||||
classLoaders = Collections.synchronizedList(new ArrayList<ClassLoader>());
|
|
||||||
synchronized(classLoaders) {
|
|
||||||
classLoaders.add(loader);
|
classLoaders.add(loader);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void removeClassLoader(ClassLoader loader) {
|
public void removeClassLoader(ClassLoader loader) {
|
||||||
if(classLoaders != null) synchronized(classLoaders) {
|
|
||||||
classLoaders.remove(loader);
|
classLoaders.remove(loader);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public List<ClassLoader> getClassLoaders(){
|
public List<ClassLoader> getClassLoaders(){
|
||||||
return classLoaders;
|
return Collections.unmodifiableList(classLoaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAssetEventListener(AssetEventListener listener) {
|
||||||
|
eventListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAssetEventListener(AssetEventListener listener) {
|
||||||
|
eventListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAssetEventListeners() {
|
||||||
|
eventListeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAssetEventListener(AssetEventListener listener){
|
public void setAssetEventListener(AssetEventListener listener){
|
||||||
eventListener = listener;
|
eventListeners.clear();
|
||||||
|
eventListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerLoader(Class<? extends AssetLoader> loader, String ... extensions){
|
public void registerLoader(Class<? extends AssetLoader> loader, String ... extensions){
|
||||||
@ -143,6 +155,14 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void unregisterLoader(Class<? extends AssetLoader> loaderClass) {
|
||||||
|
handler.removeLoader(loaderClass);
|
||||||
|
if (logger.isLoggable(Level.FINER)){
|
||||||
|
logger.log(Level.FINER, "Unregistered loader: {0}",
|
||||||
|
loaderClass.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass){
|
public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass){
|
||||||
handler.addLocator(locatorClass, rootPath);
|
handler.addLocator(locatorClass, rootPath);
|
||||||
if (logger.isLoggable(Level.FINER)){
|
if (logger.isLoggable(Level.FINER)){
|
||||||
@ -174,37 +194,13 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void clearCache(){
|
public void clearCache(){
|
||||||
cache.deleteAllAssets();
|
handler.clearCache();
|
||||||
|
if (logger.isLoggable(Level.FINER)){
|
||||||
|
logger.log(Level.FINER, "All asset caches cleared.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an asset from the cache, returns true if it was deleted
|
|
||||||
* successfully.
|
|
||||||
* <br/><br/>
|
|
||||||
* <font color="red">Thread-safe.</font>
|
|
||||||
*/
|
|
||||||
public boolean deleteFromCache(AssetKey key){
|
|
||||||
return cache.deleteFromCache(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a resource to the cache.
|
|
||||||
* <br/><br/>
|
|
||||||
* <font color="red">Thread-safe.</font>
|
|
||||||
*/
|
|
||||||
public void addToCache(AssetKey key, Object asset){
|
|
||||||
cache.addToCache(key, asset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssetInfo locateAsset(AssetKey<?> key){
|
public AssetInfo locateAsset(AssetKey<?> key){
|
||||||
if (handler.getLocatorCount() == 0){
|
|
||||||
logger.warning("There are no locators currently"+
|
|
||||||
" registered. Use AssetManager."+
|
|
||||||
"registerLocator() to register a"+
|
|
||||||
" locator.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetInfo info = handler.tryLocate(key);
|
AssetInfo info = handler.tryLocate(key);
|
||||||
if (info == null){
|
if (info == null){
|
||||||
logger.log(Level.WARNING, "Cannot locate resource: {0}", key);
|
logger.log(Level.WARNING, "Cannot locate resource: {0}", key);
|
||||||
@ -224,59 +220,40 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
if (key == null)
|
if (key == null)
|
||||||
throw new IllegalArgumentException("key cannot be null");
|
throw new IllegalArgumentException("key cannot be null");
|
||||||
|
|
||||||
if (eventListener != null)
|
for (AssetEventListener listener : eventListeners){
|
||||||
eventListener.assetRequested(key);
|
listener.assetRequested(key);
|
||||||
|
}
|
||||||
|
|
||||||
AssetKey smartKey = null;
|
AssetCache cache = handler.getCache(key.getCacheType());
|
||||||
Object o = null;
|
AssetProcessor proc = handler.getProcessor(key.getProcessorType());
|
||||||
if (key.shouldCache()){
|
|
||||||
if (key.useSmartCache()){
|
Object obj = cache != null ? cache.getFromCache(key) : null;
|
||||||
SmartAssetInfo smartInfo = cache.getFromSmartCache(key);
|
if (obj == null){
|
||||||
if (smartInfo != null){
|
// Asset not in cache, load it from file system.
|
||||||
smartKey = smartInfo.smartKey.get();
|
|
||||||
if (smartKey != null){
|
|
||||||
o = smartInfo.asset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
o = cache.getFromCache(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (o == null){
|
|
||||||
AssetLoader loader = handler.aquireLoader(key);
|
AssetLoader loader = handler.aquireLoader(key);
|
||||||
if (loader == null){
|
|
||||||
throw new IllegalStateException("No loader registered for type \"" +
|
|
||||||
key.getExtension() + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler.getLocatorCount() == 0){
|
|
||||||
throw new IllegalStateException("There are no locators currently"+
|
|
||||||
" registered. Use AssetManager."+
|
|
||||||
"registerLocator() to register a"+
|
|
||||||
" locator.");
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetInfo info = handler.tryLocate(key);
|
AssetInfo info = handler.tryLocate(key);
|
||||||
if (info == null){
|
if (info == null){
|
||||||
if (handler.getParentKey() != null && eventListener != null){
|
if (handler.getParentKey() != null){
|
||||||
// Inform event listener that an asset has failed to load.
|
// Inform event listener that an asset has failed to load.
|
||||||
// If the parent AssetLoader chooses not to propagate
|
// If the parent AssetLoader chooses not to propagate
|
||||||
// the exception, this is the only means of finding
|
// the exception, this is the only means of finding
|
||||||
// that something went wrong.
|
// that something went wrong.
|
||||||
eventListener.assetDependencyNotFound(handler.getParentKey(), key);
|
for (AssetEventListener listener : eventListeners){
|
||||||
|
listener.assetDependencyNotFound(handler.getParentKey(), key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new AssetNotFoundException(key.toString());
|
throw new AssetNotFoundException(key.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
handler.establishParentKey(key);
|
handler.establishParentKey(key);
|
||||||
o = loader.load(info);
|
obj = loader.load(info);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
|
throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
|
||||||
} finally {
|
} finally {
|
||||||
handler.releaseParentKey(key);
|
handler.releaseParentKey(key);
|
||||||
}
|
}
|
||||||
if (o == null){
|
if (obj == null){
|
||||||
throw new AssetLoadException("Error occured while loading asset \"" + key + "\" using " + loader.getClass().getSimpleName());
|
throw new AssetLoadException("Error occured while loading asset \"" + key + "\" using " + loader.getClass().getSimpleName());
|
||||||
}else{
|
}else{
|
||||||
if (logger.isLoggable(Level.FINER)){
|
if (logger.isLoggable(Level.FINER)){
|
||||||
@ -284,28 +261,39 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
new Object[]{key, loader.getClass().getSimpleName()});
|
new Object[]{key, loader.getClass().getSimpleName()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (proc != null){
|
||||||
// do processing on asset before caching
|
// do processing on asset before caching
|
||||||
o = key.postProcess(o);
|
obj = proc.postProcess(key, obj);
|
||||||
|
}
|
||||||
|
|
||||||
if (key.shouldCache())
|
if (cache != null){
|
||||||
cache.addToCache(key, o);
|
// At this point, obj should be of type T
|
||||||
|
cache.addToCache(key, (T) obj);
|
||||||
|
}
|
||||||
|
|
||||||
if (eventListener != null)
|
for (AssetEventListener listener : eventListeners){
|
||||||
eventListener.assetLoaded(key);
|
listener.assetLoaded(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// object o is the asset
|
// object obj is the original asset
|
||||||
// create an instance for user
|
// create an instance for user
|
||||||
T clone = (T) key.createClonedInstance(o);
|
T clone = (T) obj;
|
||||||
|
if (clone instanceof CloneableSmartAsset){
|
||||||
if (key.useSmartCache()){
|
if (proc == null){
|
||||||
if (smartKey != null){
|
throw new IllegalStateException("Asset implements "
|
||||||
// smart asset was already cached, use original key
|
+ "CloneableSmartAsset but doesn't "
|
||||||
((Asset)clone).setKey(smartKey);
|
+ "have processor to handle cloning");
|
||||||
}else{
|
}else{
|
||||||
// smart asset was cached on this call, use our key
|
clone = (T) proc.createClone(obj);
|
||||||
((Asset)clone).setKey(key);
|
if (cache != null && clone != obj){
|
||||||
|
cache.registerAssetClone(key, clone);
|
||||||
|
} else{
|
||||||
|
throw new IllegalStateException("Asset implements "
|
||||||
|
+ "CloneableSmartAsset but doesn't have cache or "
|
||||||
|
+ "was not cloned");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,38 +317,15 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
return (Material) loadAsset(new MaterialKey(name));
|
return (Material) loadAsset(new MaterialKey(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a texture.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param generateMipmaps Enable if applying texture to 3D objects, disable
|
|
||||||
* for GUI/HUD elements.
|
|
||||||
* @return the loaded texture
|
|
||||||
*/
|
|
||||||
public Texture loadTexture(String name, boolean generateMipmaps){
|
|
||||||
TextureKey key = new TextureKey(name, true);
|
|
||||||
key.setGenerateMips(generateMipmaps);
|
|
||||||
key.setAsCube(false);
|
|
||||||
return loadTexture(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Texture loadTexture(String name, boolean generateMipmaps, boolean flipY, boolean asCube, int aniso){
|
|
||||||
TextureKey key = new TextureKey(name, flipY);
|
|
||||||
key.setGenerateMips(generateMipmaps);
|
|
||||||
key.setAsCube(asCube);
|
|
||||||
key.setAnisotropy(aniso);
|
|
||||||
return loadTexture(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Texture loadTexture(String name){
|
public Texture loadTexture(String name){
|
||||||
return loadTexture(name, true);
|
return loadTexture(new TextureKey(name, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioData loadAudio(AudioKey key){
|
public AudioNode loadAudio(AudioKey key){
|
||||||
return (AudioData) loadAsset(key);
|
return (AudioNode) loadAsset(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioData loadAudio(String name){
|
public AudioNode loadAudio(String name){
|
||||||
return loadAudio(new AudioKey(name, false));
|
return loadAudio(new AudioKey(name, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +352,7 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
public Shader loadShader(ShaderKey key){
|
public Shader loadShader(ShaderKey key){
|
||||||
// cache abuse in method
|
// cache abuse in method
|
||||||
// that doesn't use loaders/locators
|
// that doesn't use loaders/locators
|
||||||
|
AssetCache cache = handler.getCache(SimpleAssetCache.class);
|
||||||
Shader s = (Shader) cache.getFromCache(key);
|
Shader s = (Shader) cache.getFromCache(key);
|
||||||
if (s == null){
|
if (s == null){
|
||||||
String vertName = key.getVertName();
|
String vertName = key.getVertName();
|
||||||
@ -417,5 +383,4 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
public Spatial loadModel(String name){
|
public Spatial loadModel(String name){
|
||||||
return loadModel(new ModelKey(name));
|
return loadModel(new ModelKey(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,12 @@
|
|||||||
|
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -49,40 +52,62 @@ public class ImplHandler {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ImplHandler.class.getName());
|
private static final Logger logger = Logger.getLogger(ImplHandler.class.getName());
|
||||||
|
|
||||||
private final AssetManager owner;
|
private final AssetManager assetManager;
|
||||||
|
|
||||||
private final ThreadLocal<AssetKey> parentAssetKey
|
private final ThreadLocal<AssetKey> parentAssetKey
|
||||||
= new ThreadLocal<AssetKey>();
|
= new ThreadLocal<AssetKey>();
|
||||||
|
|
||||||
private final ArrayList<ImplThreadLocal> genericLocators =
|
private final CopyOnWriteArrayList<ImplThreadLocal<AssetLocator>> locatorsList =
|
||||||
new ArrayList<ImplThreadLocal>();
|
new CopyOnWriteArrayList<ImplThreadLocal<AssetLocator>>();
|
||||||
|
|
||||||
private final HashMap<String, ImplThreadLocal> loaders =
|
private final HashMap<Class<?>, ImplThreadLocal<AssetLoader>> classToLoaderMap =
|
||||||
new HashMap<String, ImplThreadLocal>();
|
new HashMap<Class<?>, ImplThreadLocal<AssetLoader>>();
|
||||||
|
|
||||||
public ImplHandler(AssetManager owner){
|
private final ConcurrentHashMap<String, ImplThreadLocal<AssetLoader>> extensionToLoaderMap =
|
||||||
this.owner = owner;
|
new ConcurrentHashMap<String, ImplThreadLocal<AssetLoader>>();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Class<? extends AssetProcessor>, AssetProcessor> classToProcMap =
|
||||||
|
new ConcurrentHashMap<Class<? extends AssetProcessor>, AssetProcessor>();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Class<? extends AssetCache>, AssetCache> classToCacheMap =
|
||||||
|
new ConcurrentHashMap<Class<? extends AssetCache>, AssetCache>();
|
||||||
|
|
||||||
|
public ImplHandler(AssetManager assetManager){
|
||||||
|
this.assetManager = assetManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class ImplThreadLocal extends ThreadLocal {
|
protected class ImplThreadLocal<T> extends ThreadLocal {
|
||||||
|
|
||||||
private final Class<?> type;
|
private final Class<T> type;
|
||||||
private final String path;
|
private final String path;
|
||||||
|
private final String[] extensions;
|
||||||
|
|
||||||
public ImplThreadLocal(Class<?> type){
|
public ImplThreadLocal(Class<T> type, String[] extensions){
|
||||||
this.type = type;
|
this.type = type;
|
||||||
path = null;
|
this.extensions = extensions;
|
||||||
|
this.path = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImplThreadLocal(Class<?> type, String path){
|
public ImplThreadLocal(Class<T> type, String path){
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
this.extensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImplThreadLocal(Class<T> type){
|
||||||
|
this.type = type;
|
||||||
|
this.path = null;
|
||||||
|
this.extensions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getExtensions(){
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
public Class<?> getTypeClass(){
|
public Class<?> getTypeClass(){
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@ -137,27 +162,29 @@ public class ImplHandler {
|
|||||||
* access, or null if not found.
|
* access, or null if not found.
|
||||||
*/
|
*/
|
||||||
public AssetInfo tryLocate(AssetKey key){
|
public AssetInfo tryLocate(AssetKey key){
|
||||||
synchronized (genericLocators){
|
if (locatorsList.isEmpty()){
|
||||||
if (genericLocators.isEmpty())
|
logger.warning("There are no locators currently"+
|
||||||
|
" registered. Use AssetManager."+
|
||||||
|
"registerLocator() to register a"+
|
||||||
|
" locator.");
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
for (ImplThreadLocal local : genericLocators){
|
for (ImplThreadLocal local : locatorsList){
|
||||||
AssetLocator locator = (AssetLocator) local.get();
|
AssetLocator locator = (AssetLocator) local.get();
|
||||||
if (local.getPath() != null){
|
if (local.getPath() != null){
|
||||||
locator.setRootPath((String) local.getPath());
|
locator.setRootPath((String) local.getPath());
|
||||||
}
|
}
|
||||||
AssetInfo info = locator.locate(owner, key);
|
AssetInfo info = locator.locate(assetManager, key);
|
||||||
if (info != null)
|
if (info != null)
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLocatorCount(){
|
public int getLocatorCount(){
|
||||||
synchronized (genericLocators){
|
return locatorsList.size();
|
||||||
return genericLocators.size();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,44 +193,120 @@ public class ImplHandler {
|
|||||||
* @return AssetLoader registered with addLoader.
|
* @return AssetLoader registered with addLoader.
|
||||||
*/
|
*/
|
||||||
public AssetLoader aquireLoader(AssetKey key){
|
public AssetLoader aquireLoader(AssetKey key){
|
||||||
synchronized (loaders){
|
// No need to synchronize() against map, its concurrent
|
||||||
ImplThreadLocal local = loaders.get(key.getExtension());
|
ImplThreadLocal local = extensionToLoaderMap.get(key.getExtension());
|
||||||
if (local != null){
|
if (local == null){
|
||||||
AssetLoader loader = (AssetLoader) local.get();
|
throw new IllegalStateException("No loader registered for type \"" +
|
||||||
return loader;
|
key.getExtension() + "\"");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return (AssetLoader) local.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCache(){
|
||||||
|
// The iterator of the values collection is thread safe
|
||||||
|
for (AssetCache cache : classToCacheMap.values()){
|
||||||
|
cache.clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends AssetCache> T getCache(Class<T> cacheClass) {
|
||||||
|
if (cacheClass == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T cache = (T) classToCacheMap.get(cacheClass);
|
||||||
|
if (cache == null) {
|
||||||
|
synchronized (classToCacheMap) {
|
||||||
|
cache = (T) classToCacheMap.get(cacheClass);
|
||||||
|
if (cache == null) {
|
||||||
|
try {
|
||||||
|
cache = cacheClass.newInstance();
|
||||||
|
classToCacheMap.put(cacheClass, cache);
|
||||||
|
} catch (InstantiationException ex) {
|
||||||
|
throw new IllegalArgumentException("The cache class cannot"
|
||||||
|
+ " be created, ensure it has empty constructor", ex);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalArgumentException("The cache class cannot "
|
||||||
|
+ "be accessed", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLoader(final Class<?> loaderType, String ... extensions){
|
public <T extends AssetProcessor> T getProcessor(Class<T> procClass){
|
||||||
ImplThreadLocal local = new ImplThreadLocal(loaderType);
|
if (procClass == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
T proc = (T) classToProcMap.get(procClass);
|
||||||
|
if (proc == null){
|
||||||
|
synchronized(classToProcMap){
|
||||||
|
proc = (T) classToProcMap.get(procClass);
|
||||||
|
if (proc == null) {
|
||||||
|
try {
|
||||||
|
proc = procClass.newInstance();
|
||||||
|
classToProcMap.put(procClass, proc);
|
||||||
|
} catch (InstantiationException ex) {
|
||||||
|
throw new IllegalArgumentException("The processor class cannot"
|
||||||
|
+ " be created, ensure it has empty constructor", ex);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalArgumentException("The processor class cannot "
|
||||||
|
+ "be accessed", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLoader(final Class<? extends AssetLoader> loaderType, String ... extensions){
|
||||||
|
// Synchronized access must be used for any ops on classToLoaderMap
|
||||||
|
ImplThreadLocal local = new ImplThreadLocal(loaderType, extensions);
|
||||||
for (String extension : extensions){
|
for (String extension : extensions){
|
||||||
extension = extension.toLowerCase();
|
extension = extension.toLowerCase();
|
||||||
synchronized (loaders){
|
synchronized (classToLoaderMap){
|
||||||
loaders.put(extension, local);
|
classToLoaderMap.put(loaderType, local);
|
||||||
|
extensionToLoaderMap.put(extension, local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLocator(final Class<?> locatorType, String rootPath){
|
public void removeLoader(final Class<? extends AssetLoader> loaderType){
|
||||||
ImplThreadLocal local = new ImplThreadLocal(locatorType, rootPath);
|
// Synchronized access must be used for any ops on classToLoaderMap
|
||||||
synchronized (genericLocators){
|
// Find the loader ImplThreadLocal for this class
|
||||||
genericLocators.add(local);
|
synchronized (classToLoaderMap){
|
||||||
|
ImplThreadLocal local = classToLoaderMap.get(loaderType);
|
||||||
|
// Remove it from the class->loader map
|
||||||
|
classToLoaderMap.remove(loaderType);
|
||||||
|
// Remove it from the extension->loader map
|
||||||
|
for (String extension : local.getExtensions()){
|
||||||
|
extensionToLoaderMap.remove(extension);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLocator(final Class<?> locatorType, String rootPath){
|
public void addLocator(final Class<? extends AssetLocator> locatorType, String rootPath){
|
||||||
synchronized (genericLocators){
|
locatorsList.add(new ImplThreadLocal(locatorType, rootPath));
|
||||||
Iterator<ImplThreadLocal> it = genericLocators.iterator();
|
}
|
||||||
|
|
||||||
|
public void removeLocator(final Class<? extends AssetLocator> locatorType, String rootPath){
|
||||||
|
ArrayList<ImplThreadLocal<AssetLocator>> locatorsToRemove = new ArrayList<ImplThreadLocal<AssetLocator>>();
|
||||||
|
Iterator<ImplThreadLocal<AssetLocator>> it = locatorsList.iterator();
|
||||||
|
|
||||||
while (it.hasNext()){
|
while (it.hasNext()){
|
||||||
ImplThreadLocal locator = it.next();
|
ImplThreadLocal locator = it.next();
|
||||||
if (locator.getPath().equals(rootPath) &&
|
if (locator.getPath().equals(rootPath) &&
|
||||||
locator.getTypeClass().equals(locatorType)){
|
locator.getTypeClass().equals(locatorType)){
|
||||||
it.remove();
|
//it.remove();
|
||||||
}
|
// copy on write list doesn't support iterator remove,
|
||||||
}
|
// must use temporary list
|
||||||
|
locatorsToRemove.add(locator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locatorsList.removeAll(locatorsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2010 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
import com.jme3.asset.cache.WeakRefCloneAssetCache;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for loading {@link Material materials} only (not material definitions).
|
* Used for loading {@link Material materials} only (not material definitions!).
|
||||||
|
* Material instances use cloneable smart asset management so that they and any
|
||||||
|
* referenced textures will be collected when all instances of the material
|
||||||
|
* become unreachable.
|
||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public class MaterialKey extends AssetKey {
|
public class MaterialKey extends AssetKey<Material> {
|
||||||
|
|
||||||
public MaterialKey(String name) {
|
public MaterialKey(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
@ -17,13 +54,12 @@ public class MaterialKey extends AssetKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useSmartCache(){
|
public Class<? extends AssetCache> getCacheType() {
|
||||||
return true;
|
return WeakRefCloneAssetCache.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createClonedInstance(Object asset){
|
public Class<? extends AssetProcessor> getProcessorType() {
|
||||||
Material mat = (Material) asset;
|
return CloneableAssetProcessor.class;
|
||||||
return mat.clone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,18 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
import com.jme3.asset.cache.WeakRefCloneAssetCache;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Used to load model files, such as OBJ or Blender models.
|
||||||
|
* This uses cloneable smart asset management, so that when all clones of
|
||||||
|
* this model become unreachable, the original asset is purged from the cache,
|
||||||
|
* allowing textures, materials, shaders, etc referenced by the model to
|
||||||
|
* become collected.
|
||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
@ -47,15 +53,14 @@ public class ModelKey extends AssetKey<Spatial> {
|
|||||||
public ModelKey() {
|
public ModelKey() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useSmartCache(){
|
public Class<? extends AssetCache> getCacheType(){
|
||||||
return true;
|
return WeakRefCloneAssetCache.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createClonedInstance(Object asset){
|
public Class<? extends AssetProcessor> getProcessorType(){
|
||||||
Spatial model = (Spatial) asset;
|
return CloneableAssetProcessor.class;
|
||||||
return model.clone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,15 +31,29 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
import com.jme3.asset.cache.WeakRefCloneAssetCache;
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
|
import com.jme3.texture.Image;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.Texture.Type;
|
import com.jme3.texture.Texture.Type;
|
||||||
import com.jme3.texture.*;
|
import com.jme3.texture.TextureProcessor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to load textures from image files such as JPG or PNG.
|
||||||
|
* Note that texture loaders actually load the asset as an {@link Image}
|
||||||
|
* object, which is then converted to a {@link Texture} in the
|
||||||
|
* {@link TextureProcessor#postProcess(com.jme3.asset.AssetKey, java.lang.Object) }
|
||||||
|
* method. Since textures are cloneable smart assets, the texture stored
|
||||||
|
* in the cache will be collected when all clones of the texture become
|
||||||
|
* unreachable.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
public class TextureKey extends AssetKey<Texture> {
|
public class TextureKey extends AssetKey<Texture> {
|
||||||
|
|
||||||
private boolean generateMips;
|
private boolean generateMips;
|
||||||
@ -64,56 +78,17 @@ public class TextureKey extends AssetKey<Texture> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + (flipY ? " (Flipped)" : "") + (asCube ? " (Cube)" : "") + (generateMips ? " (Mipmaped)" : "");
|
return name + (flipY ? " (Flipped)" : "") + (asCube ? " (Cube)" : "") + (generateMips ? " (Mipmapped)" : "");
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable smart caching for textures
|
|
||||||
* @return true to enable smart cache
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean useSmartCache() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createClonedInstance(Object asset) {
|
public Class<? extends AssetCache> getCacheType(){
|
||||||
Texture tex = (Texture) asset;
|
return WeakRefCloneAssetCache.class;
|
||||||
return tex.createSimpleClone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcess(Object asset) {
|
public Class<? extends AssetProcessor> getProcessorType(){
|
||||||
Image img = (Image) asset;
|
return TextureProcessor.class;
|
||||||
if (img == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture tex;
|
|
||||||
if (isAsCube()) {
|
|
||||||
if (isFlipY()) {
|
|
||||||
// also flip -y and +y image in cubemap
|
|
||||||
ByteBuffer pos_y = img.getData(2);
|
|
||||||
img.setData(2, img.getData(3));
|
|
||||||
img.setData(3, pos_y);
|
|
||||||
}
|
|
||||||
tex = new TextureCubeMap();
|
|
||||||
} else if (isAsTexture3D()) {
|
|
||||||
tex = new Texture3D();
|
|
||||||
} else {
|
|
||||||
tex = new Texture2D();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable mipmaps if image has them
|
|
||||||
// or generate them if requested by user
|
|
||||||
if (img.hasMipmaps() || isGenerateMips()) {
|
|
||||||
tex.setMinFilter(Texture.MinFilter.Trilinear);
|
|
||||||
}
|
|
||||||
|
|
||||||
tex.setAnisotropicFilter(getAnisotropy());
|
|
||||||
tex.setName(getName());
|
|
||||||
tex.setImage(img);
|
|
||||||
return tex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFlipY() {
|
public boolean isFlipY() {
|
||||||
@ -152,14 +127,6 @@ public class TextureKey extends AssetKey<Texture> {
|
|||||||
this.asTexture3D = asTexture3D;
|
this.asTexture3D = asTexture3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (!(other instanceof TextureKey)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.equals(other) && isFlipY() == ((TextureKey) other).isFlipY();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getTextureTypeHint() {
|
public Type getTextureTypeHint() {
|
||||||
return textureTypeHint;
|
return textureTypeHint;
|
||||||
}
|
}
|
||||||
@ -168,7 +135,53 @@ public class TextureKey extends AssetKey<Texture> {
|
|||||||
this.textureTypeHint = textureTypeHint;
|
this.textureTypeHint = textureTypeHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final TextureKey other = (TextureKey) obj;
|
||||||
|
if (!super.equals(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.generateMips != other.generateMips) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.flipY != other.flipY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.asCube != other.asCube) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.asTexture3D != other.asTexture3D) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.anisotropy != other.anisotropy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.textureTypeHint != other.textureTypeHint) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 7;
|
||||||
|
hash = 17 * hash + (super.hashCode());
|
||||||
|
hash = 17 * hash + (this.generateMips ? 1 : 0);
|
||||||
|
hash = 17 * hash + (this.flipY ? 1 : 0);
|
||||||
|
hash = 17 * hash + (this.asCube ? 1 : 0);
|
||||||
|
hash = 17 * hash + (this.asTexture3D ? 1 : 0);
|
||||||
|
hash = 17 * hash + this.anisotropy;
|
||||||
|
hash = 17 * hash + (this.textureTypeHint != null ? this.textureTypeHint.hashCode() : 0);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
75
engine/src/core/com/jme3/asset/cache/AssetCache.java
vendored
Normal file
75
engine/src/core/com/jme3/asset/cache/AssetCache.java
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package com.jme3.asset.cache;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AssetCache</code> is an interface for asset caches.
|
||||||
|
* Allowing storage of loaded resources in order to improve their access time
|
||||||
|
* if they are requested again in a short period of time.
|
||||||
|
* Depending on the asset type and how it is used, a specialized
|
||||||
|
* caching method can be selected that is most appropriate for that asset type.
|
||||||
|
* The asset cache must be thread safe.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public interface AssetCache {
|
||||||
|
/**
|
||||||
|
* Adds an asset to the cache.
|
||||||
|
* Once added, it should be possible to retrieve the asset
|
||||||
|
* by using the {@link #getFromCache(com.jme3.asset.AssetKey) } method.
|
||||||
|
* However the caching criteria may at some point choose that the asset
|
||||||
|
* should be removed from the cache to save memory, in that case,
|
||||||
|
* {@link #getFromCache(com.jme3.asset.AssetKey) } will return null.
|
||||||
|
* <p><font color="red">Thread-Safe</font>
|
||||||
|
*
|
||||||
|
* @param <T> The type of the asset to cache.
|
||||||
|
* @param key The asset key that can be used to look up the asset.
|
||||||
|
* @param obj The asset data to cache.
|
||||||
|
*/
|
||||||
|
public <T> void addToCache(AssetKey<T> key, T obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should be called by the asset manager when it has successfully
|
||||||
|
* acquired a cached asset (with {@link #getFromCache(com.jme3.asset.AssetKey) })
|
||||||
|
* and cloned it for use.
|
||||||
|
* <p><font color="red">Thread-Safe</font>
|
||||||
|
*
|
||||||
|
* @param <T> The type of the asset to register.
|
||||||
|
* @param key The asset key of the loaded asset (used to retrieve from cache)
|
||||||
|
* @param clone The <strong>clone</strong> of the asset retrieved from
|
||||||
|
* the cache.
|
||||||
|
*/
|
||||||
|
public <T> void registerAssetClone(AssetKey<T> key, T clone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an asset from the cache.
|
||||||
|
* It is possible to add an asset to the cache using
|
||||||
|
* {@link #addToCache(com.jme3.asset.AssetKey, java.lang.Object) }.
|
||||||
|
* The asset may be removed from the cache automatically even if
|
||||||
|
* it was added previously, in that case, this method will return null.
|
||||||
|
* <p><font color="red">Thread-Safe</font>
|
||||||
|
*
|
||||||
|
* @param <T> The type of the asset to retrieve
|
||||||
|
* @param key The key used to lookup the asset.
|
||||||
|
* @return The asset that was previously cached, or null if not found.
|
||||||
|
*/
|
||||||
|
public <T> T getFromCache(AssetKey<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an asset from the cache.
|
||||||
|
* <p><font color="red">Thread-Safe</font>
|
||||||
|
*
|
||||||
|
* @param key The asset key to find the asset to delete.
|
||||||
|
* @return True if the asset was successfully found in the cache
|
||||||
|
* and removed.
|
||||||
|
*/
|
||||||
|
public boolean deleteFromCache(AssetKey key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all assets from the cache.
|
||||||
|
* <p><font color="red">Thread-Safe</font>
|
||||||
|
*/
|
||||||
|
public void clearCache();
|
||||||
|
}
|
41
engine/src/core/com/jme3/asset/cache/SimpleAssetCache.java
vendored
Normal file
41
engine/src/core/com/jme3/asset/cache/SimpleAssetCache.java
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package com.jme3.asset.cache;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>SimpleAssetCache</code> is an asset cache
|
||||||
|
* that caches assets without any automatic removal policy. The user
|
||||||
|
* is expected to manually call {@link #deleteFromCache(com.jme3.asset.AssetKey) }
|
||||||
|
* to delete any assets.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public class SimpleAssetCache implements AssetCache {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<AssetKey, Object> keyToAssetMap = new ConcurrentHashMap<AssetKey, Object>();
|
||||||
|
|
||||||
|
public <T> void addToCache(AssetKey<T> key, T obj) {
|
||||||
|
keyToAssetMap.put(key, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void registerAssetClone(AssetKey<T> key, T clone) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getFromCache(AssetKey<T> key) {
|
||||||
|
return (T) keyToAssetMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteFromCache(AssetKey key) {
|
||||||
|
return keyToAssetMap.remove(key) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCache() {
|
||||||
|
keyToAssetMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
88
engine/src/core/com/jme3/asset/cache/WeakRefAssetCache.java
vendored
Normal file
88
engine/src/core/com/jme3/asset/cache/WeakRefAssetCache.java
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package com.jme3.asset.cache;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetProcessor;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A garbage collector bound asset cache that handles non-clonable objects.
|
||||||
|
* This cache assumes that the asset given to the user is the same asset
|
||||||
|
* that has been stored in the cache, in other words,
|
||||||
|
* {@link AssetProcessor#createClone(java.lang.Object) } for that asset
|
||||||
|
* returns the same object as the argument.
|
||||||
|
* This implementation will remove the asset from the cache
|
||||||
|
* once the asset is no longer referenced in user code and memory is low,
|
||||||
|
* e.g. the VM feels like purging the weak references for that asset.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public class WeakRefAssetCache implements AssetCache {
|
||||||
|
|
||||||
|
private final ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<AssetKey, AssetRef> assetCache
|
||||||
|
= new ConcurrentHashMap<AssetKey, AssetRef>();
|
||||||
|
|
||||||
|
private static class AssetRef extends WeakReference<Object> {
|
||||||
|
|
||||||
|
private final AssetKey assetKey;
|
||||||
|
|
||||||
|
public AssetRef(AssetKey assetKey, Object originalAsset, ReferenceQueue<Object> refQueue){
|
||||||
|
super(originalAsset, refQueue);
|
||||||
|
this.assetKey = assetKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeCollectedAssets(){
|
||||||
|
int removedAssets = 0;
|
||||||
|
for (AssetRef ref; (ref = (AssetRef)refQueue.poll()) != null;){
|
||||||
|
// Asset was collected, note that at this point the asset cache
|
||||||
|
// might not even have this asset anymore, it is OK.
|
||||||
|
if (assetCache.remove(ref.assetKey) != null){
|
||||||
|
removedAssets ++;
|
||||||
|
//System.out.println("WeakRefAssetCache: The asset " + ref.assetKey + " was purged from the cache");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removedAssets >= 1) {
|
||||||
|
// System.out.println("WeakRefAssetCache: " + removedAssets + " assets were purged from the cache.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void addToCache(AssetKey<T> key, T obj) {
|
||||||
|
removeCollectedAssets();
|
||||||
|
|
||||||
|
// NOTE: Some thread issues can hapen if another
|
||||||
|
// thread is loading an asset with the same key ..
|
||||||
|
AssetRef ref = new AssetRef(key, obj, refQueue);
|
||||||
|
assetCache.put(key, ref);
|
||||||
|
|
||||||
|
// Texture t = (Texture) obj;
|
||||||
|
// Image i = t.getImage();
|
||||||
|
// System.out.println("add to cache " + System.identityHashCode(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getFromCache(AssetKey<T> key) {
|
||||||
|
AssetRef ref = assetCache.get(key);
|
||||||
|
if (ref != null){
|
||||||
|
return (T) ref.get();
|
||||||
|
}else{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteFromCache(AssetKey key) {
|
||||||
|
return assetCache.remove(key) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCache() {
|
||||||
|
assetCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void registerAssetClone(AssetKey<T> key, T clone) {
|
||||||
|
// Texture t = (Texture) clone;
|
||||||
|
// System.out.println("clonable asset " + System.identityHashCode(t.getImage()));
|
||||||
|
//throw new UnsupportedOperationException("Cannot use this cache for cloneable assets");
|
||||||
|
}
|
||||||
|
}
|
116
engine/src/core/com/jme3/asset/cache/WeakRefCloneAssetCache.java
vendored
Normal file
116
engine/src/core/com/jme3/asset/cache/WeakRefCloneAssetCache.java
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package com.jme3.asset.cache;
|
||||||
|
|
||||||
|
import com.jme3.asset.CloneableSmartAsset;
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <codeWeakRefCloneAssetCache</code> caches cloneable assets in a weak-key
|
||||||
|
* cache, allowing them to be collected when memory is low.
|
||||||
|
* The cache stores weak references to the asset keys, so that
|
||||||
|
* when all clones of the original asset are collected, will cause the
|
||||||
|
* asset to be automatically removed from the cache.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public class WeakRefCloneAssetCache implements AssetCache {
|
||||||
|
|
||||||
|
private static final class SmartCachedAsset {
|
||||||
|
|
||||||
|
WeakReference<AssetKey> key;
|
||||||
|
CloneableSmartAsset asset;
|
||||||
|
|
||||||
|
public SmartCachedAsset(CloneableSmartAsset originalAsset, AssetKey originalKey) {
|
||||||
|
this.key = new WeakReference<AssetKey>(originalKey);
|
||||||
|
this.asset = originalAsset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final WeakHashMap<AssetKey, SmartCachedAsset> smartCache
|
||||||
|
= new WeakHashMap<AssetKey, SmartCachedAsset>();
|
||||||
|
|
||||||
|
private final ThreadLocal<ArrayDeque<AssetKey>> assetLoadStack
|
||||||
|
= new ThreadLocal<ArrayDeque<AssetKey>>() {
|
||||||
|
@Override
|
||||||
|
protected ArrayDeque<AssetKey> initialValue() {
|
||||||
|
return new ArrayDeque<AssetKey>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public <T> void addToCache(AssetKey<T> key, T obj) {
|
||||||
|
CloneableSmartAsset asset = (CloneableSmartAsset) obj;
|
||||||
|
|
||||||
|
// No circular references, since the original asset is
|
||||||
|
// strongly referenced, we don't want the key strongly referenced.
|
||||||
|
asset.setKey(null);
|
||||||
|
|
||||||
|
// Place the asset in the cache with a weak ref to the key.
|
||||||
|
synchronized (smartCache) {
|
||||||
|
smartCache.put(key, new SmartCachedAsset(asset, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the original key used to load the asset
|
||||||
|
// so that it can be set on the clone later
|
||||||
|
ArrayDeque<AssetKey> loadStack = assetLoadStack.get();
|
||||||
|
loadStack.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void registerAssetClone(AssetKey<T> key, T clone) {
|
||||||
|
ArrayDeque<AssetKey> loadStack = assetLoadStack.get();
|
||||||
|
((CloneableSmartAsset)clone).setKey(loadStack.pop());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getFromCache(AssetKey<T> key) {
|
||||||
|
SmartCachedAsset smartInfo;
|
||||||
|
synchronized (smartCache){
|
||||||
|
smartInfo = smartCache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smartInfo == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// NOTE: Optimization so that registerAssetClone()
|
||||||
|
// can check this and determine that the asset clone
|
||||||
|
// belongs to the asset retrieved here.
|
||||||
|
AssetKey keyForTheClone = smartInfo.key.get();
|
||||||
|
if (keyForTheClone == null){
|
||||||
|
// The asset was JUST collected by GC
|
||||||
|
// (between here and smartCache.get)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent original key from getting collected
|
||||||
|
// while an asset is loaded for it.
|
||||||
|
ArrayDeque<AssetKey> loadStack = assetLoadStack.get();
|
||||||
|
loadStack.push(keyForTheClone);
|
||||||
|
|
||||||
|
return (T) smartInfo.asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteFromCache(AssetKey key) {
|
||||||
|
ArrayDeque<AssetKey> loadStack = assetLoadStack.get();
|
||||||
|
|
||||||
|
if (!loadStack.isEmpty()){
|
||||||
|
throw new UnsupportedOperationException("Cache cannot be modified"
|
||||||
|
+ "while assets are being loaded");
|
||||||
|
}
|
||||||
|
synchronized (smartCache) {
|
||||||
|
return smartCache.remove(key) != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCache() {
|
||||||
|
ArrayDeque<AssetKey> loadStack = assetLoadStack.get();
|
||||||
|
|
||||||
|
if (!loadStack.isEmpty()){
|
||||||
|
throw new UnsupportedOperationException("Cache cannot be modified"
|
||||||
|
+ "while assets are being loaded");
|
||||||
|
}
|
||||||
|
synchronized (smartCache) {
|
||||||
|
smartCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -106,5 +106,4 @@ public abstract class AudioData extends NativeObject {
|
|||||||
this.bitsPerSample = bitsPerSample;
|
this.bitsPerSample = bitsPerSample;
|
||||||
this.sampleRate = sampleRate;
|
this.sampleRate = sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@
|
|||||||
package com.jme3.audio;
|
package com.jme3.audio;
|
||||||
|
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetProcessor;
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
import com.jme3.asset.cache.WeakRefAssetCache;
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
@ -44,7 +47,7 @@ import java.io.IOException;
|
|||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public class AudioKey extends AssetKey<AudioData> {
|
public class AudioKey extends AssetKey<AudioNode> {
|
||||||
|
|
||||||
private boolean stream;
|
private boolean stream;
|
||||||
private boolean streamCache;
|
private boolean streamCache;
|
||||||
@ -114,8 +117,49 @@ public class AudioKey extends AssetKey<AudioData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldCache(){
|
public Class<? extends AssetCache> getCacheType() {
|
||||||
return !stream && !streamCache;
|
if ((stream && streamCache) || !stream) {
|
||||||
|
// Use non-cloning cache
|
||||||
|
return WeakRefAssetCache.class;
|
||||||
|
} else {
|
||||||
|
// Disable caching for streaming audio
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final AudioKey other = (AudioKey) obj;
|
||||||
|
if (!super.equals(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.stream != other.stream) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.streamCache != other.streamCache) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 7;
|
||||||
|
hash = 67 * hash + (super.hashCode());
|
||||||
|
hash = 67 * hash + (this.stream ? 1 : 0);
|
||||||
|
hash = 67 * hash + (this.streamCache ? 1 : 0);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends AssetProcessor> getProcessorType() {
|
||||||
|
return AudioProcessor.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
19
engine/src/core/com/jme3/audio/AudioProcessor.java
Normal file
19
engine/src/core/com/jme3/audio/AudioProcessor.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.jme3.audio;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetProcessor;
|
||||||
|
|
||||||
|
public class AudioProcessor implements AssetProcessor{
|
||||||
|
|
||||||
|
public Object postProcess(AssetKey key, Object obj) {
|
||||||
|
AudioKey audioKey = (AudioKey) key;
|
||||||
|
AudioData audioData = (AudioData) obj;
|
||||||
|
return new AudioNode(audioData, audioKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createClone(Object obj) {
|
||||||
|
AudioNode node = (AudioNode) obj;
|
||||||
|
return node.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,7 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.material;
|
package com.jme3.material;
|
||||||
|
|
||||||
import com.jme3.asset.Asset;
|
import com.jme3.asset.CloneableSmartAsset;
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
@ -66,7 +66,7 @@ import java.util.logging.Logger;
|
|||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public class Material implements Asset, Cloneable, Savable {
|
public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||||
|
|
||||||
// Version #2: Fixed issue with RenderState.apply*** flags not getting exported
|
// Version #2: Fixed issue with RenderState.apply*** flags not getting exported
|
||||||
public static final int SAVABLE_VERSION = 2;
|
public static final int SAVABLE_VERSION = 2;
|
||||||
|
15
engine/src/core/com/jme3/material/MaterialProcessor.java
Normal file
15
engine/src/core/com/jme3/material/MaterialProcessor.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.jme3.material;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetProcessor;
|
||||||
|
|
||||||
|
public class MaterialProcessor implements AssetProcessor {
|
||||||
|
|
||||||
|
public Object postProcess(AssetKey key, Object obj) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createClone(Object obj) {
|
||||||
|
return ((Material) obj).clone();
|
||||||
|
}
|
||||||
|
}
|
@ -31,8 +31,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene;
|
package com.jme3.scene;
|
||||||
|
|
||||||
import com.jme3.asset.Asset;
|
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.CloneableSmartAsset;
|
||||||
import com.jme3.bounding.BoundingVolume;
|
import com.jme3.bounding.BoundingVolume;
|
||||||
import com.jme3.collision.Collidable;
|
import com.jme3.collision.Collidable;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
@ -63,7 +63,7 @@ import java.util.logging.Logger;
|
|||||||
* @author Joshua Slack
|
* @author Joshua Slack
|
||||||
* @version $Revision: 4075 $, $Data$
|
* @version $Revision: 4075 $, $Data$
|
||||||
*/
|
*/
|
||||||
public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
|
public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
|
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ package com.jme3.texture;
|
|||||||
|
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
import com.jme3.asset.AssetNotFoundException;
|
import com.jme3.asset.AssetNotFoundException;
|
||||||
import com.jme3.asset.Asset;
|
import com.jme3.asset.CloneableSmartAsset;
|
||||||
import com.jme3.asset.TextureKey;
|
import com.jme3.asset.TextureKey;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import com.jme3.util.PlaceholderAssets;
|
import com.jme3.util.PlaceholderAssets;
|
||||||
@ -57,7 +57,7 @@ import java.util.logging.Logger;
|
|||||||
* @author Joshua Slack
|
* @author Joshua Slack
|
||||||
* @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $
|
* @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $
|
||||||
*/
|
*/
|
||||||
public abstract class Texture implements Asset, Savable, Cloneable {
|
public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable {
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
|
|
||||||
|
49
engine/src/core/com/jme3/texture/TextureProcessor.java
Normal file
49
engine/src/core/com/jme3/texture/TextureProcessor.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package com.jme3.texture;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetProcessor;
|
||||||
|
import com.jme3.asset.TextureKey;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class TextureProcessor implements AssetProcessor {
|
||||||
|
|
||||||
|
public Object postProcess(AssetKey key, Object obj) {
|
||||||
|
TextureKey texKey = (TextureKey) key;
|
||||||
|
Image img = (Image) obj;
|
||||||
|
if (img == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture tex;
|
||||||
|
if (texKey.isAsCube()) {
|
||||||
|
if (texKey.isFlipY()) {
|
||||||
|
// also flip -y and +y image in cubemap
|
||||||
|
ByteBuffer pos_y = img.getData(2);
|
||||||
|
img.setData(2, img.getData(3));
|
||||||
|
img.setData(3, pos_y);
|
||||||
|
}
|
||||||
|
tex = new TextureCubeMap();
|
||||||
|
} else if (texKey.isAsTexture3D()) {
|
||||||
|
tex = new Texture3D();
|
||||||
|
} else {
|
||||||
|
tex = new Texture2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable mipmaps if image has them
|
||||||
|
// or generate them if requested by user
|
||||||
|
if (img.hasMipmaps() || texKey.isGenerateMips()) {
|
||||||
|
tex.setMinFilter(Texture.MinFilter.Trilinear);
|
||||||
|
}
|
||||||
|
|
||||||
|
tex.setAnisotropicFilter(texKey.getAnisotropy());
|
||||||
|
tex.setName(texKey.getName());
|
||||||
|
tex.setImage(img);
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createClone(Object obj) {
|
||||||
|
Texture tex = (Texture) obj;
|
||||||
|
return tex.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,35 +32,42 @@
|
|||||||
|
|
||||||
package jme3test.asset;
|
package jme3test.asset;
|
||||||
|
|
||||||
import com.jme3.asset.Asset;
|
|
||||||
import com.jme3.asset.AssetCache;
|
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.AssetProcessor;
|
||||||
|
import com.jme3.asset.CloneableAssetProcessor;
|
||||||
|
import com.jme3.asset.CloneableSmartAsset;
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
import com.jme3.asset.cache.SimpleAssetCache;
|
||||||
|
import com.jme3.asset.cache.WeakRefAssetCache;
|
||||||
|
import com.jme3.asset.cache.WeakRefCloneAssetCache;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TestAssetCache {
|
public class TestAssetCache {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep references to loaded assets
|
* Counter for asset keys
|
||||||
*/
|
*/
|
||||||
private final static boolean KEEP_REFERENCES = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable smart cache use
|
|
||||||
*/
|
|
||||||
private final static boolean USE_SMART_CACHE = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable cloneable asset use
|
|
||||||
*/
|
|
||||||
private final static boolean CLONEABLE_ASSET = true;
|
|
||||||
|
|
||||||
private static int counter = 0;
|
private static int counter = 0;
|
||||||
|
|
||||||
private static class DummyData implements Asset {
|
/**
|
||||||
|
* Dummy data is an asset having 10 KB to put a dent in the garbage collector
|
||||||
|
*/
|
||||||
|
private static class DummyData implements CloneableSmartAsset {
|
||||||
|
|
||||||
private AssetKey key;
|
private AssetKey key;
|
||||||
private byte[] data = new byte[10000];
|
private byte[] data = new byte[10 * 1024];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object clone(){
|
||||||
|
try {
|
||||||
|
DummyData clone = (DummyData) super.clone();
|
||||||
|
clone.data = data.clone();
|
||||||
|
return clone;
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getData(){
|
public byte[] getData(){
|
||||||
return data;
|
return data;
|
||||||
@ -75,67 +82,64 @@ public class TestAssetCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SmartKey extends AssetKey {
|
/**
|
||||||
|
* Dummy key is indexed by a generated ID
|
||||||
|
*/
|
||||||
|
private static class DummyKey extends AssetKey<DummyData> implements Cloneable {
|
||||||
|
|
||||||
public SmartKey(){
|
private int id = 0;
|
||||||
|
|
||||||
|
public DummyKey(){
|
||||||
super(".");
|
super(".");
|
||||||
counter++;
|
id = counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DummyKey(int id){
|
||||||
|
super(".");
|
||||||
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode(){
|
public int hashCode(){
|
||||||
return 0;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other){
|
public boolean equals(Object other){
|
||||||
return false;
|
return ((DummyKey)other).id == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useSmartCache(){
|
public DummyKey clone(){
|
||||||
return true;
|
return new DummyKey(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createClonedInstance(Object asset){
|
public String toString() {
|
||||||
DummyData data = new DummyData();
|
return "ID=" + id;
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DumbKey extends AssetKey {
|
private static void runTest(boolean cloneAssets, boolean smartCache, boolean keepRefs, int limit) {
|
||||||
|
counter = 0;
|
||||||
|
List<Object> refs = new ArrayList<Object>(limit);
|
||||||
|
|
||||||
public DumbKey(){
|
AssetCache cache;
|
||||||
super(".");
|
AssetProcessor proc = null;
|
||||||
counter++;
|
|
||||||
|
if (cloneAssets) {
|
||||||
|
proc = new CloneableAssetProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (smartCache) {
|
||||||
public int hashCode(){
|
if (cloneAssets) {
|
||||||
return 0;
|
cache = new WeakRefCloneAssetCache();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object createClonedInstance(Object asset){
|
|
||||||
if (CLONEABLE_ASSET){
|
|
||||||
DummyData data = new DummyData();
|
|
||||||
return data;
|
|
||||||
} else {
|
} else {
|
||||||
return asset;
|
cache = new WeakRefAssetCache();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cache = new SimpleAssetCache();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args){
|
|
||||||
List<Object> refs = new ArrayList<Object>(5000);
|
|
||||||
|
|
||||||
AssetCache cache = new AssetCache();
|
|
||||||
|
|
||||||
System.gc();
|
System.gc();
|
||||||
System.gc();
|
System.gc();
|
||||||
@ -144,28 +148,81 @@ public class TestAssetCache {
|
|||||||
|
|
||||||
long memory = Runtime.getRuntime().freeMemory();
|
long memory = Runtime.getRuntime().freeMemory();
|
||||||
|
|
||||||
while (true){
|
while (counter < limit){
|
||||||
AssetKey key;
|
// Create a key
|
||||||
|
DummyKey key = new DummyKey();
|
||||||
|
|
||||||
if (USE_SMART_CACHE){
|
// Create some data
|
||||||
key = new SmartKey();
|
DummyData data = new DummyData();
|
||||||
}else{
|
|
||||||
key = new DumbKey();
|
// Post process the data before placing it in the cache
|
||||||
|
if (proc != null){
|
||||||
|
data = (DummyData) proc.postProcess(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.key != null){
|
||||||
|
// Keeping a hard reference to the key in the cache
|
||||||
|
// means the asset will never be collected => bug
|
||||||
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
DummyData data = new DummyData();
|
|
||||||
cache.addToCache(key, data);
|
cache.addToCache(key, data);
|
||||||
|
|
||||||
if (KEEP_REFERENCES){
|
// Get the asset from the cache
|
||||||
|
AssetKey<DummyData> keyToGet = key.clone();
|
||||||
|
|
||||||
|
// NOTE: Commented out because getFromCache leaks the original key
|
||||||
|
// DummyData someLoaded = (DummyData) cache.getFromCache(keyToGet);
|
||||||
|
// if (someLoaded != data){
|
||||||
|
// // Failed to get the same asset from the cache => bug
|
||||||
|
// // Since a hard reference to the key is kept,
|
||||||
|
// // it cannot be collected at this point.
|
||||||
|
// throw new AssertionError();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Clone the asset
|
||||||
|
if (proc != null){
|
||||||
|
// Data is now the clone!
|
||||||
|
data = (DummyData) proc.createClone(data);
|
||||||
|
if (smartCache) {
|
||||||
|
// Registering a clone is only needed
|
||||||
|
// if smart cache is used.
|
||||||
|
cache.registerAssetClone(keyToGet, data);
|
||||||
|
// The clone of the asset must have the same key as the original
|
||||||
|
// otherwise => bug
|
||||||
|
if (data.key != key){
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep references to the asset => *should* prevent
|
||||||
|
// collections of the asset in the cache thus causing
|
||||||
|
// an out of memory error.
|
||||||
|
if (keepRefs){
|
||||||
|
// Prevent the saved references from taking too much memory ..
|
||||||
|
if (cloneAssets) {
|
||||||
|
data.data = null;
|
||||||
|
}
|
||||||
refs.add(data);
|
refs.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((counter % 100) == 0){
|
if ((counter % 1000) == 0){
|
||||||
long newMem = Runtime.getRuntime().freeMemory();
|
long newMem = Runtime.getRuntime().freeMemory();
|
||||||
System.out.println("Allocated objects: " + counter);
|
System.out.println("Allocated objects: " + counter);
|
||||||
System.out.println("Allocated memory: " + ((memory - newMem)/1024) + "K" );
|
System.out.println("Allocated memory: " + ((memory - newMem)/(1024*1024)) + " MB" );
|
||||||
memory = newMem;
|
memory = newMem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args){
|
||||||
|
// Test cloneable smart asset
|
||||||
|
System.out.println("====== Running Cloneable Smart Asset Test ======");
|
||||||
|
runTest(true, true, false, 100000);
|
||||||
|
|
||||||
|
// Test non-cloneable smart asset
|
||||||
|
System.out.println("====== Running Non-cloneable Smart Asset Test ======");
|
||||||
|
runTest(false, true, false, 100000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user