diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt index e9b135fbef..30f489f951 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt @@ -74,6 +74,7 @@ class ModelViewer { private var swapChain: SwapChain? = null private var assetLoader: AssetLoader private var resourceLoader: ResourceLoader + private val readyRenderables = IntArray(128) // add up to 128 entities at a time private val eyePos = DoubleArray(3) private val target = DoubleArray(3) @@ -207,13 +208,7 @@ class ModelViewer { resourceLoader.asyncUpdateLoad() // Add renderable entities to the scene as they become ready. - asset?.let { - var entity = 0 - val popRenderable = {entity = it.popRenderable(); entity != 0} - while (popRenderable()) { - scene.addEntity(entity) - } - } + asset?.let { populateScene(it) } // Extract the camera basis from the helper and push it to the Filament camera. cameraManipulator.getLookAt(eyePos, target, upward) @@ -229,6 +224,14 @@ class ModelViewer { } } + private fun populateScene(asset: FilamentAsset) { + var count = 0 + val popRenderables = {count = asset.popRenderables(readyRenderables); count != 0} + while (popRenderables()) { + scene.addEntities(readyRenderables.take(count).toIntArray()) + } + } + private fun addDetachListener(view: android.view.View) { view.addOnAttachStateChangeListener(object : android.view.View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: android.view.View?) {} diff --git a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp index 83e706023c..e27b522ff1 100644 --- a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp +++ b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp @@ -36,6 +36,17 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nPopRenderable(JNIEnv*, jc return asset->popRenderable().getId(); } +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_gltfio_FilamentAsset_nPopRenderables(JNIEnv* env, jclass, + jlong nativeAsset, jintArray result) { + FilamentAsset* asset = (FilamentAsset*) nativeAsset; + jsize available = env->GetArrayLength(result); + Entity* entities = (Entity*) env->GetIntArrayElements(result, nullptr); + size_t retval = asset->popRenderables(entities, available); + env->ReleaseIntArrayElements(result, (jint*) entities, 0); + return retval; +} + extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_gltfio_FilamentAsset_nGetEntityCount(JNIEnv*, jclass, jlong nativeAsset) { @@ -47,8 +58,10 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_gltfio_FilamentAsset_nGetEntities(JNIEnv* env, jclass, jlong nativeAsset, jintArray result) { FilamentAsset* asset = (FilamentAsset*) nativeAsset; + jsize available = env->GetArrayLength(result); Entity* entities = (Entity*) env->GetIntArrayElements(result, nullptr); - std::copy_n(asset->getEntities(), asset->getEntityCount(), entities); + std::copy_n(asset->getEntities(), + std::min(available, (jsize) asset->getEntityCount()), entities); env->ReleaseIntArrayElements(result, (jint*) entities, 0); } diff --git a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java index 3e6a228e3f..e7e4dcfa9f 100644 --- a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java +++ b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java @@ -17,6 +17,7 @@ package com.google.android.filament.gltfio; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.filament.Box; import com.google.android.filament.Entity; @@ -76,6 +77,16 @@ public class FilamentAsset { return nPopRenderable(mNativeObject); } + /** + * Pops one or more renderables off the queue, or returns the available number. + * + * Returns the number of entities written into the given array. If the given array + * is null, returns the number of available renderables. + */ + public int popRenderables(@Nullable @Entity int[] entities) { + return nPopRenderables(mNativeObject, entities); + } + /** * Gets the list of entities, one for each glTF node. * @@ -143,6 +154,7 @@ public class FilamentAsset { private static native int nGetRoot(long nativeAsset); private static native int nPopRenderable(long nativeAsset); + private static native int nPopRenderables(long nativeAsset, int[] result); private static native int nGetEntityCount(long nativeAsset); private static native void nGetEntities(long nativeAsset, int[] result); private static native void nGetBoundingBox(long nativeAsset, float[] box); diff --git a/libs/gltfio/include/gltfio/FilamentAsset.h b/libs/gltfio/include/gltfio/FilamentAsset.h index 65f66c1dfb..723faab22e 100644 --- a/libs/gltfio/include/gltfio/FilamentAsset.h +++ b/libs/gltfio/include/gltfio/FilamentAsset.h @@ -76,7 +76,8 @@ public: * Pops a ready renderable off the queue, or returns 0 if no renderables have become ready. * * NOTE: To determine the progress percentage or completion status, please use - * ResourceLoader#asyncGetLoadProgress. + * ResourceLoader#asyncGetLoadProgress. To get the number of ready renderables, + * please use popRenderables(). * * This method allows clients to progressively add the asset's renderables to the scene as * textures gradually become ready through asynchronous loading. For example, on every frame @@ -84,10 +85,22 @@ public: * * while (utils::Entity e = popRenderable()) { scene.addEntity(e); } * - * See ResourceLoader#asyncBeginLoad. + * \see ResourceLoader#asyncBeginLoad + * \see popRenderables() */ utils::Entity popRenderable() noexcept; + /** + * Pops up to "count" ready renderables off the queue, or returns the available number. + * + * The given pointer should either be null or point to memory that can hold up to count + * entities. If the pointer is null, returns the number of available renderables. Otherwise + * returns the number of entities that have been written. + * + * \see ResourceLoader#asyncBeginLoad + */ + size_t popRenderables(utils::Entity* entities, size_t count) noexcept; + /** Gets all material instances. These are already bound to renderables. */ const filament::MaterialInstance* const* getMaterialInstances() const noexcept; diff --git a/libs/gltfio/include/gltfio/SimpleViewer.h b/libs/gltfio/include/gltfio/SimpleViewer.h index c66a6d9917..9ea746ca37 100644 --- a/libs/gltfio/include/gltfio/SimpleViewer.h +++ b/libs/gltfio/include/gltfio/SimpleViewer.h @@ -257,27 +257,26 @@ SimpleViewer::~SimpleViewer() { } void SimpleViewer::populateScene(FilamentAsset* asset, bool scale) { - if (mAsset == asset) { - while (utils::Entity e = mAsset->popRenderable()) { - mScene->addEntity(e); + if (mAsset != asset) { + removeAsset(); + mAsset = asset; + if (!asset) { + mAnimator = nullptr; + return; + } + mAnimator = asset->getAnimator(); + if (scale) { + auto& tcm = mEngine->getTransformManager(); + auto root = tcm.getInstance(mAsset->getRoot()); + filament::math::mat4f transform = fitIntoUnitCube(mAsset->getBoundingBox()); + tcm.setTransform(root, transform); } - return; } - removeAsset(); - mAsset = asset; - if (!asset) { - mAnimator = nullptr; - return; - } - mAnimator = asset->getAnimator(); - if (scale) { - auto& tcm = mEngine->getTransformManager(); - auto root = tcm.getInstance(mAsset->getRoot()); - filament::math::mat4f transform = fitIntoUnitCube(mAsset->getBoundingBox()); - tcm.setTransform(root, transform); - } - while (utils::Entity e = mAsset->popRenderable()) { - mScene->addEntity(e); + + static constexpr int kNumAvailable = 128; + utils::Entity renderables[kNumAvailable]; + while (size_t numWritten = mAsset->popRenderables(renderables, kNumAvailable)) { + mScene->addEntities(renderables, numWritten); } } diff --git a/libs/gltfio/src/DependencyGraph.cpp b/libs/gltfio/src/DependencyGraph.cpp index 3997c66ab5..56736a2bc7 100644 --- a/libs/gltfio/src/DependencyGraph.cpp +++ b/libs/gltfio/src/DependencyGraph.cpp @@ -21,13 +21,16 @@ using namespace utils; namespace gltfio { -Entity DependencyGraph::popReadyRenderable() noexcept { - if (mReadyRenderables.empty()) { - return Entity(); +size_t DependencyGraph::popRenderables(Entity* result, size_t count) noexcept { + if (result == nullptr) { + return mReadyRenderables.size(); } - auto oldest = mReadyRenderables.front(); - mReadyRenderables.pop(); - return oldest; + size_t numWritten = 0; + while (numWritten < count && !mReadyRenderables.empty()) { + result[numWritten++] = mReadyRenderables.front(); + mReadyRenderables.pop(); + } + return numWritten; } void DependencyGraph::addEdge(Entity entity, MaterialInstance* mi) { diff --git a/libs/gltfio/src/DependencyGraph.h b/libs/gltfio/src/DependencyGraph.h index d1c750f55d..25ae276c11 100644 --- a/libs/gltfio/src/DependencyGraph.h +++ b/libs/gltfio/src/DependencyGraph.h @@ -62,8 +62,10 @@ public: using Material = filament::MaterialInstance; using Entity = utils::Entity; - // Returns a ready-to-render entity, or 0 if no new entities have become renderable. - Entity popReadyRenderable() noexcept; + // Pops up to "count" ready-to-render entities off the queue. + // If "result" is non-null, returns the number of written items. + // If "result" is null, returns the number of available entities. + size_t popRenderables(Entity* result, size_t count) noexcept; // These are called during the initial asset loader phase. void addEdge(Entity entity, Material* material); diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index 9df662ff03..93f1760415 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -94,8 +94,8 @@ struct FFilamentAsset : public FilamentAsset { return mRoot; } - utils::Entity popRenderable() noexcept { - return mDependencyGraph.popReadyRenderable(); + size_t popRenderables(utils::Entity* entities, size_t count) noexcept { + return mDependencyGraph.popRenderables(entities, count); } size_t getMaterialInstanceCount() const noexcept { diff --git a/libs/gltfio/src/FilamentAsset.cpp b/libs/gltfio/src/FilamentAsset.cpp index 4a7b570f51..568b43ba16 100644 --- a/libs/gltfio/src/FilamentAsset.cpp +++ b/libs/gltfio/src/FilamentAsset.cpp @@ -36,7 +36,13 @@ Entity FilamentAsset::getRoot() const noexcept { } Entity FilamentAsset::popRenderable() noexcept { - return upcast(this)->popRenderable(); + Entity result[1]; + const bool empty = !popRenderables(result, 1); + return empty ? Entity() : result[0]; +} + +size_t FilamentAsset::popRenderables(Entity* result, size_t count) noexcept { + return upcast(this)->popRenderables(result, count); } size_t FilamentAsset::getMaterialInstanceCount() const noexcept {