/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.assets; import com.badlogic.gdx.assets.loaders.AssetLoader; import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader; import com.badlogic.gdx.assets.loaders.SynchronousAssetLoader; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.Logger; import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.async.AsyncExecutor; import com.badlogic.gdx.utils.async.AsyncResult; import com.badlogic.gdx.utils.async.AsyncTask; /** Responsible for loading an asset through an {@link AssetLoader} based on an {@link AssetDescriptor}. * * @author mzechner */ class AssetLoadingTask implements AsyncTask<Void> { AssetManager manager; final AssetDescriptor assetDesc; final AssetLoader loader; final AsyncExecutor executor; final long startTime; volatile boolean asyncDone = false; volatile boolean dependenciesLoaded = false; volatile Array<AssetDescriptor> dependencies; volatile AsyncResult<Void> depsFuture = null; volatile AsyncResult<Void> loadFuture = null; volatile Object asset = null; int ticks = 0; volatile boolean cancel = false; public AssetLoadingTask (AssetManager manager, AssetDescriptor assetDesc, AssetLoader loader, AsyncExecutor threadPool) { this.manager = manager; this.assetDesc = assetDesc; this.loader = loader; this.executor = threadPool; startTime = manager.log.getLevel() == Logger.DEBUG ? TimeUtils.nanoTime() : 0; } /** Loads parts of the asset asynchronously if the loader is an {@link AsynchronousAssetLoader}. */ @Override public Void call () throws Exception { AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader; if (dependenciesLoaded == false) { dependencies = asyncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); if (dependencies != null) { removeDuplicates(dependencies); manager.injectDependencies(assetDesc.fileName, dependencies); } else { // if we have no dependencies, we load the async part of the task immediately. asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); asyncDone = true; } } else { asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); } return null; } /** Updates the loading of the asset. In case the asset is loaded with an {@link AsynchronousAssetLoader}, the loaders * {@link AsynchronousAssetLoader#loadAsync(AssetManager, String, FileHandle, AssetLoaderParameters)} method is first called on * a worker thread. Once this method returns, the rest of the asset is loaded on the rendering thread via * {@link AsynchronousAssetLoader#loadSync(AssetManager, String, FileHandle, AssetLoaderParameters)}. * @return true in case the asset was fully loaded, false otherwise * @throws GdxRuntimeException */ public boolean update () { ticks++; if (loader instanceof SynchronousAssetLoader) { handleSyncLoader(); } else { handleAsyncLoader(); } return asset != null; } private void handleSyncLoader () { SynchronousAssetLoader syncLoader = (SynchronousAssetLoader)loader; if (!dependenciesLoaded) { dependenciesLoaded = true; dependencies = syncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); if (dependencies == null) { asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); return; } removeDuplicates(dependencies); manager.injectDependencies(assetDesc.fileName, dependencies); } else { asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); } } private void handleAsyncLoader () { AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader; if (!dependenciesLoaded) { if (depsFuture == null) { depsFuture = executor.submit(this); } else { if (depsFuture.isDone()) { try { depsFuture.get(); } catch (Exception e) { throw new GdxRuntimeException("Couldn't load dependencies of asset: " + assetDesc.fileName, e); } dependenciesLoaded = true; if (asyncDone) { asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); } } } } else { if (loadFuture == null && !asyncDone) { loadFuture = executor.submit(this); } else { if (asyncDone) { asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); } else if (loadFuture.isDone()) { try { loadFuture.get(); } catch (Exception e) { throw new GdxRuntimeException("Couldn't load asset: " + assetDesc.fileName, e); } asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params); } } } } private FileHandle resolve (AssetLoader loader, AssetDescriptor assetDesc) { if (assetDesc.file == null) assetDesc.file = loader.resolve(assetDesc.fileName); return assetDesc.file; } public Object getAsset () { return asset; } private void removeDuplicates(Array<AssetDescriptor> array) { boolean ordered = array.ordered; array.ordered = true; for (int i = 0; i < array.size; ++i) { final String fn = array.get(i).fileName; final Class type = array.get(i).type; for (int j = array.size - 1; j > i; --j) { if (type == array.get(j).type && fn.equals(array.get(j).fileName)) array.removeIndex(j); } } array.ordered = ordered; } }