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;