From a64b1eccdf37dde0eb038606225cf69b13b8659d Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 15 Jun 2022 17:08:56 -0700 Subject: [PATCH] add API to enable/disable auto-instancing globally. auto-instancing can have some overhead, so when it is known that the scene doesn't have identical primitives, it is better to disable it. (disabled by default). Also add some missing bindings for `enableAccurateTranslations`. --- RELEASE_NOTES.md | 1 + .../filament-android/src/main/cpp/Engine.cpp | 12 ++++++ .../com/google/android/filament/Engine.java | 40 +++++++++++++++++++ .../src/main/cpp/AutomationEngine.cpp | 2 + .../filament/utils/AutomationEngine.java | 1 + filament/include/filament/Engine.h | 22 ++++++++++ filament/src/Engine.cpp | 10 +++++ filament/src/RenderPass.cpp | 4 +- filament/src/details/Engine.h | 9 +++++ libs/viewer/include/viewer/Settings.h | 1 + libs/viewer/src/Settings.cpp | 3 ++ libs/viewer/src/ViewerGui.cpp | 2 + samples/gltf_viewer.cpp | 2 + samples/image_viewer.cpp | 1 + web/filament-js/jsbindings.cpp | 6 +++ 15 files changed, 115 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8df65eda30..ddaedbf8df 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -11,6 +11,7 @@ A new header is inserted each time a *tag* is created. - backend: added support for EGL on linux (headless) - uberz tool: add --append and --template arguments. - matc tool: add --template argument. +- engine: add support for automatic instancing. Must be enabled with `Engine::setAutomaticInstancingEnabled(bool)` ## v1.24.0 diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index ea861db3a9..882cdeb939 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -316,3 +316,15 @@ Java_com_google_android_filament_Engine_nGetEntityManager(JNIEnv*, jclass, jlong Engine* engine = (Engine*) nativeEngine; return (jlong) &engine->getEntityManager(); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Engine_nSetAutomaticInstancingEnabled(JNIEnv*, jclass, jlong nativeEngine, jboolean enable) { + Engine* engine = (Engine*) nativeEngine; + engine->setAutomaticInstancingEnabled(enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, jclass, jlong nativeEngine) { + Engine* engine = (Engine*) nativeEngine; + return (jboolean)engine->isAutomaticInstancingEnabled(); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 2f2cc9abfb..58b0482f09 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -258,6 +258,44 @@ public class Engine { return sBackendValues[(int) nGetBackend(getNativeObject())]; } + /** + * Helper to enable accurate translations. + * If you need this Engine to handle a very large world space, one way to achieve this + * automatically is to enable accurate translations in the TransformManager. This helper + * provides a convenient way of doing that. + * This is typically called once just after creating the Engine. + */ + public void enableAccurateTranslations() { + getTransformManager().setAccurateTranslationsEnabled(true); + } + + /** + * Enables or disables automatic instancing of render primitives. Instancing of render primitive + * can greatly reduce CPU overhead but requires the instanced primitives to be identical + * (i.e. use the same geometry) and use the same MaterialInstance. If it is known that the + * scene doesn't contain any identical primitives, automatic instancing can have some + * overhead and it is then best to disable it. + * + * Disabled by default. + * + * @param enable true to enable, false to disable automatic instancing. + * + * @see RenderableManager + * @see MaterialInstance + */ + public void setAutomaticInstancingEnabled(boolean enable) { + nSetAutomaticInstancingEnabled(getNativeObject(), enable); + } + + /** + * @return true if automatic instancing is enabled, false otherwise. + * @see setAutomaticInstancingEnabled + */ + public boolean isAutomaticInstancingEnabled() { + return nIsAutomaticInstancingEnabled(getNativeObject()); + } + + // SwapChain /** @@ -720,4 +758,6 @@ public class Engine { private static native long nGetRenderableManager(long nativeEngine); private static native long nGetJobSystem(long nativeEngine); private static native long nGetEntityManager(long nativeEngine); + private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable); + private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine); } diff --git a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp index 143cb32d04..a7f2cb8766 100644 --- a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp +++ b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp @@ -169,6 +169,7 @@ Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv const jfieldID cameraFocalLength = env->GetFieldID(klass, "cameraFocalLength", "F"); const jfieldID cameraFocusDistance = env->GetFieldID(klass, "cameraFocusDistance", "F"); const jfieldID autoScaleEnabled = env->GetFieldID(klass, "autoScaleEnabled", "Z"); + const jfieldID autoInstancingEnabled = env->GetFieldID(klass, "autoInstancingEnabled", "Z"); env->SetFloatField(result, cameraAperture, options.cameraAperture); env->SetFloatField(result, cameraSpeed, options.cameraSpeed); @@ -179,6 +180,7 @@ Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv env->SetFloatField(result, cameraFocalLength, options.cameraFocalLength); env->SetFloatField(result, cameraFocusDistance, options.cameraFocusDistance); env->SetBooleanField(result, autoScaleEnabled, options.autoScaleEnabled); + env->SetBooleanField(result, autoInstancingEnabled, options.autoInstancingEnabled); } extern "C" JNIEXPORT jlong JNICALL diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java index 4383c2161d..151b37fe52 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java @@ -103,6 +103,7 @@ public class AutomationEngine { public float cameraFocalLength = 28.0f; public float cameraFocusDistance = 0.0f; public boolean autoScaleEnabled = true; + public boolean autoInstancingEnabled = false; } /** diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 0a31013f66..b47a902e30 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -335,6 +335,28 @@ public: */ void enableAccurateTranslations() noexcept; + /** + * Enables or disables automatic instancing of render primitives. Instancing of render + * primitives can greatly reduce CPU overhead but requires the instanced primitives to be + * identical (i.e. use the same geometry) and use the same MaterialInstance. If it is known + * that the scene doesn't contain any identical primitives, automatic instancing can have some + * overhead and it is then best to disable it. + * + * Disabled by default. + * + * @param enable true to enable, false to disable automatic instancing. + * + * @see RenderableManager + * @see MaterialInstance + */ + void setAutomaticInstancingEnabled(bool enable) noexcept; + + /** + * @return true if automatic instancing is enabled, false otherwise. + * @see setAutomaticInstancingEnabled + */ + bool isAutomaticInstancingEnabled() const noexcept; + /** * Creates a SwapChain from the given Operating System's native window handle. * diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 0b4b291525..e2904ac789 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -31,6 +31,8 @@ #include "details/Texture.h" #include "details/VertexBuffer.h" #include "details/View.h" +#include "filament/Engine.h" + #include @@ -251,4 +253,12 @@ void Engine::pumpMessageQueues() { upcast(this)->pumpMessageQueues(); } +void Engine::setAutomaticInstancingEnabled(bool enable) noexcept { + upcast(this)->setAutomaticInstancingEnabled(enable); +} + +bool Engine::isAutomaticInstancingEnabled() const noexcept { + return upcast(this)->isAutomaticInstancingEnabled(); +} + } // namespace filament diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index fa35b9205a..e3fd97a741 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -185,7 +185,9 @@ void RenderPass::sortCommands() noexcept { resize(uint32_t(last - mCommandBegin)); - instanceify(); + if (mEngine.isAutomaticInstancingEnabled()) { + instanceify(); + } } void RenderPass::instanceify() noexcept { diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 3d37a9b9f6..3abafcb367 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -350,6 +350,14 @@ public: getDriver().purge(); } + void setAutomaticInstancingEnabled(bool enable) noexcept { + mAutomaticInstancingEnabled = enable; + } + + bool isAutomaticInstancingEnabled() const noexcept { + return mAutomaticInstancingEnabled; + } + backend::Handle getOneTexture() const { return mDummyOneTexture; } backend::Handle getZeroTexture() const { return mDummyZeroTexture; } backend::Handle getOneTextureArray() const { return mDummyOneTextureArray; } @@ -382,6 +390,7 @@ private: Backend mBackend; Platform* mPlatform = nullptr; bool mOwnPlatform = false; + bool mAutomaticInstancingEnabled = false; void* mSharedGLContext = nullptr; backend::Handle mFullScreenTriangleRph; FVertexBuffer* mFullScreenTriangleVb = nullptr; diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 45c9ce6df4..350c244e25 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -213,6 +213,7 @@ struct ViewerOptions { float cameraFocalLength = 28.0f; float cameraFocusDistance = 10.0f; bool autoScaleEnabled = true; + bool autoInstancingEnabled = false; }; struct Settings { diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index cc5b88ca67..f1b5977ed6 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -428,6 +428,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewerOp i = parse(tokens, i + 1, jsonChunk, &out->cameraFocalLength); } else if (compare(tok, jsonChunk, "cameraFocusDistance") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->cameraFocusDistance); + } else if (compare(tok, jsonChunk, "autoInstancingEnabled") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->autoInstancingEnabled); } else if (compare(tok, jsonChunk, "autoScaleEnabled") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->autoScaleEnabled); } else { @@ -744,6 +746,7 @@ static std::ostream& operator<<(std::ostream& out, const ViewerOptions& in) { << "\"backgroundColor\": " << (in.backgroundColor) << ",\n" << "\"cameraFocalLength\": " << (in.cameraFocalLength) << ",\n" << "\"cameraFocusDistance\": " << (in.cameraFocusDistance) << ",\n" + << "\"autoInstancingEnabled\": " << to_string(in.autoInstancingEnabled) << ",\n" << "\"autoScaleEnabled\": " << to_string(in.autoScaleEnabled) << "\n" << "}"; } diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index bc530f8da8..b000af2352 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -887,6 +887,8 @@ void ViewerGui::updateUserInterface() { updateRootTransform(); } + ImGui::Checkbox("Automatic instancing", &mSettings.viewer.autoInstancingEnabled); + ImGui::Checkbox("Show skybox", &mSettings.viewer.skyboxEnabled); ImGui::ColorEdit3("Background color", &mSettings.viewer.backgroundColor.r); diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 1b649ae0cf..dcba259552 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -729,6 +729,8 @@ int main(int argc, char** argv) { rcm.setLayerMask(instance, 0xff, viewerOptions.groundPlaneEnabled ? 0xff : 0x00); + engine->setAutomaticInstancingEnabled(viewerOptions.autoInstancingEnabled); + // Note that this focal length might be different from the slider value because the // automation engine applies Camera::computeEffectiveFocalLength when DoF is enabled. FilamentApp::get().getCameraFocalLength() = viewerOptions.cameraFocalLength; diff --git a/samples/image_viewer.cpp b/samples/image_viewer.cpp index 9355a80b0f..de2f3a038a 100644 --- a/samples/image_viewer.cpp +++ b/samples/image_viewer.cpp @@ -292,6 +292,7 @@ int main(int argc, char** argv) { app.engine = engine; app.viewer = new ViewerGui(engine, scene, view, 410); app.viewer->getSettings().viewer.autoScaleEnabled = false; + app.viewer->getSettings().viewer.autoInstancingEnabled = true; app.viewer->getSettings().view.bloom.enabled = false; app.viewer->getSettings().view.ssao.enabled = false; app.viewer->getSettings().view.dithering = Dithering::NONE; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index f09b9ea6f7..ce7263d892 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -432,6 +432,12 @@ class_("Engine") return Engine::create(); }, allow_raw_pointers()) + .function("enableAccurateTranslations", &Engine::enableAccurateTranslations) + + .function("setAutomaticInstancingEnabled", &Engine::setAutomaticInstancingEnabled) + + .function("isAutomaticInstancingEnabled", &Engine::isAutomaticInstancingEnabled) + .function("_execute", EMBIND_LAMBDA(void, (Engine* engine), { EM_ASM_INT({ const handle = window.filament_contextHandle;