From b21dfd5ea27e0cd88532dfd4854b9f71242eb36c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 16 Apr 2024 12:12:20 -0700 Subject: [PATCH] add a couple way of validating MaterialInstances (#7754) * add a couple way of validating MaterialInstances BUGS=[333907416] * Update filament/include/filament/Engine.h Co-authored-by: Powei Feng * Update filament/include/filament/Engine.h Co-authored-by: Powei Feng --------- Co-authored-by: Powei Feng --- .../filament-android/src/main/cpp/Engine.cpp | 15 +++++ .../com/google/android/filament/Engine.java | 20 ++++++ filament/include/filament/Engine.h | 63 +++++++++++++----- filament/src/Engine.cpp | 51 +++++++++------ filament/src/details/Engine.cpp | 65 +++++++++++++------ filament/src/details/Engine.h | 41 ++++++------ web/filament-js/jsbindings.cpp | 6 ++ 7 files changed, 184 insertions(+), 77 deletions(-) diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index 2677e2f8fa..713baa53ed 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -342,6 +342,21 @@ Java_com_google_android_filament_Engine_nIsValidMaterial(JNIEnv*, jclass, return (jboolean)engine->isValid((Material*)nativeMaterial); } +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_Engine_nIsValidMaterialInstance(JNIEnv*, jclass, + jlong nativeEngine, jlong nativeMaterial, jlong nativeMaterialInstance) { + Engine* engine = (Engine *)nativeEngine; + return (jboolean)engine->isValid((Material*)nativeMaterial, + (MaterialInstance*)nativeMaterialInstance); +} + +extern "C" JNIEXPORT jboolean JNICALL + Java_com_google_android_filament_Engine_nIsValidExpensiveMaterialInstance(JNIEnv*, jclass, + jlong nativeEngine, jlong nativeMaterialInstance) { + Engine* engine = (Engine *)nativeEngine; + return (jboolean)engine->isValidExpensive((MaterialInstance*)nativeMaterialInstance); +} + extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_Engine_nIsValidSkybox(JNIEnv*, jclass, jlong nativeEngine, jlong nativeSkybox) { 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 97301df980..a7ec77a71c 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 @@ -849,6 +849,24 @@ public class Engine { return nIsValidMaterial(getNativeObject(), object.getNativeObject()); } + /** + * Returns whether the object is valid. + * @param object Object to check for validity + * @return returns true if the specified object is valid. + */ + public boolean isValidMaterialInstance(@NonNull Material ma, MaterialInstance mi) { + return nIsValidMaterialInstance(getNativeObject(), ma.getNativeObject(), mi.getNativeObject()); + } + + /** + * Returns whether the object is valid. + * @param object Object to check for validity + * @return returns true if the specified object is valid. + */ + public boolean isValidExpensiveMaterialInstance(@NonNull MaterialInstance object) { + return nIsValidExpensiveMaterialInstance(getNativeObject(), object.getNativeObject()); + } + /** * Returns whether the object is valid. * @param object Object to check for validity @@ -1291,6 +1309,8 @@ public class Engine { private static native boolean nIsValidSkinningBuffer(long nativeEngine, long nativeSkinningBuffer); private static native boolean nIsValidIndirectLight(long nativeEngine, long nativeIndirectLight); private static native boolean nIsValidMaterial(long nativeEngine, long nativeMaterial); + private static native boolean nIsValidMaterialInstance(long nativeEngine, long nativeMaterial, long nativeMaterialInstance); + private static native boolean nIsValidExpensiveMaterialInstance(long nativeEngine, long nativeMaterialInstance); private static native boolean nIsValidSkybox(long nativeEngine, long nativeSkybox); private static native boolean nIsValidColorGrading(long nativeEngine, long nativeColorGrading); private static native boolean nIsValidTexture(long nativeEngine, long nativeTexture); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index b741d3abd9..033cb8066f 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -800,24 +800,51 @@ public: bool destroy(const InstanceBuffer* UTILS_NULLABLE p); //!< Destroys an InstanceBuffer object. void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity - bool isValid(const BufferObject* UTILS_NULLABLE p); //!< Tells whether a BufferObject object is valid - bool isValid(const VertexBuffer* UTILS_NULLABLE p); //!< Tells whether an VertexBuffer object is valid - bool isValid(const Fence* UTILS_NULLABLE p); //!< Tells whether a Fence object is valid - bool isValid(const IndexBuffer* UTILS_NULLABLE p); //!< Tells whether an IndexBuffer object is valid - bool isValid(const SkinningBuffer* UTILS_NULLABLE p); //!< Tells whether a SkinningBuffer object is valid - bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Tells whether a MorphTargetBuffer object is valid - bool isValid(const IndirectLight* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid - bool isValid(const Material* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid - bool isValid(const Renderer* UTILS_NULLABLE p); //!< Tells whether a Renderer object is valid - bool isValid(const Scene* UTILS_NULLABLE p); //!< Tells whether a Scene object is valid - bool isValid(const Skybox* UTILS_NULLABLE p); //!< Tells whether a SkyBox object is valid - bool isValid(const ColorGrading* UTILS_NULLABLE p); //!< Tells whether a ColorGrading object is valid - bool isValid(const SwapChain* UTILS_NULLABLE p); //!< Tells whether a SwapChain object is valid - bool isValid(const Stream* UTILS_NULLABLE p); //!< Tells whether a Stream object is valid - bool isValid(const Texture* UTILS_NULLABLE p); //!< Tells whether a Texture object is valid - bool isValid(const RenderTarget* UTILS_NULLABLE p); //!< Tells whether a RenderTarget object is valid - bool isValid(const View* UTILS_NULLABLE p); //!< Tells whether a View object is valid - bool isValid(const InstanceBuffer* UTILS_NULLABLE p); //!< Tells whether an InstanceBuffer object is valid + /** Tells whether a BufferObject object is valid */ + bool isValid(const BufferObject* UTILS_NULLABLE p) const; + /** Tells whether an VertexBuffer object is valid */ + bool isValid(const VertexBuffer* UTILS_NULLABLE p) const; + /** Tells whether a Fence object is valid */ + bool isValid(const Fence* UTILS_NULLABLE p) const; + /** Tells whether an IndexBuffer object is valid */ + bool isValid(const IndexBuffer* UTILS_NULLABLE p) const; + /** Tells whether a SkinningBuffer object is valid */ + bool isValid(const SkinningBuffer* UTILS_NULLABLE p) const; + /** Tells whether a MorphTargetBuffer object is valid */ + bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p) const; + /** Tells whether an IndirectLight object is valid */ + bool isValid(const IndirectLight* UTILS_NULLABLE p) const; + /** Tells whether an Material object is valid */ + bool isValid(const Material* UTILS_NULLABLE p) const; + /** Tells whether an MaterialInstance object is valid. Use this if you already know + * which Material this MaterialInstance belongs to. DO NOT USE getMaterial(), this would + * defeat the purpose of validating the MaterialInstance. + */ + bool isValid(const Material* UTILS_NONNULL m, const MaterialInstance* UTILS_NULLABLE p) const; + /** Tells whether an MaterialInstance object is valid. Use this if the Material the + * MaterialInstance belongs to is not known. This method can be expensive. + */ + bool isValidExpensive(const MaterialInstance* UTILS_NULLABLE p) const; + /** Tells whether a Renderer object is valid */ + bool isValid(const Renderer* UTILS_NULLABLE p) const; + /** Tells whether a Scene object is valid */ + bool isValid(const Scene* UTILS_NULLABLE p) const; + /** Tells whether a SkyBox object is valid */ + bool isValid(const Skybox* UTILS_NULLABLE p) const; + /** Tells whether a ColorGrading object is valid */ + bool isValid(const ColorGrading* UTILS_NULLABLE p) const; + /** Tells whether a SwapChain object is valid */ + bool isValid(const SwapChain* UTILS_NULLABLE p) const; + /** Tells whether a Stream object is valid */ + bool isValid(const Stream* UTILS_NULLABLE p) const; + /** Tells whether a Texture object is valid */ + bool isValid(const Texture* UTILS_NULLABLE p) const; + /** Tells whether a RenderTarget object is valid */ + bool isValid(const RenderTarget* UTILS_NULLABLE p) const; + /** Tells whether a View object is valid */ + bool isValid(const View* UTILS_NULLABLE p) const; + /** Tells whether an InstanceBuffer object is valid */ + bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const; /** * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index ffb85ae70a..d0042f6332 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -31,18 +31,25 @@ #include "details/Texture.h" #include "details/VertexBuffer.h" #include "details/View.h" -#include "filament/Engine.h" +#include #include #include #include +#include +#include + using namespace utils; namespace filament { +namespace backend { +class Platform; +} + using namespace math; using namespace backend; @@ -196,58 +203,64 @@ void Engine::destroy(Entity e) { downcast(this)->destroy(e); } -bool Engine::isValid(const BufferObject* p) { +bool Engine::isValid(const BufferObject* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const VertexBuffer* p) { +bool Engine::isValid(const VertexBuffer* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Fence* p) { +bool Engine::isValid(const Fence* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const IndexBuffer* p) { +bool Engine::isValid(const IndexBuffer* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const SkinningBuffer* p) { +bool Engine::isValid(const SkinningBuffer* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const MorphTargetBuffer* p) { +bool Engine::isValid(const MorphTargetBuffer* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const IndirectLight* p) { +bool Engine::isValid(const IndirectLight* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Material* p) { +bool Engine::isValid(const Material* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Renderer* p) { +bool Engine::isValid(const Material* m, const MaterialInstance* p) const { + return downcast(this)->isValid(downcast(m), downcast(p)); +} +bool Engine::isValidExpensive(const MaterialInstance* p) const { + return downcast(this)->isValidExpensive(downcast(p)); +} +bool Engine::isValid(const Renderer* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Scene* p) { +bool Engine::isValid(const Scene* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Skybox* p) { +bool Engine::isValid(const Skybox* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const ColorGrading* p) { +bool Engine::isValid(const ColorGrading* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const SwapChain* p) { +bool Engine::isValid(const SwapChain* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Stream* p) { +bool Engine::isValid(const Stream* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const Texture* p) { +bool Engine::isValid(const Texture* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const RenderTarget* p) { +bool Engine::isValid(const RenderTarget* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const View* p) { +bool Engine::isValid(const View* p) const { return downcast(this)->isValid(downcast(p)); } -bool Engine::isValid(const InstanceBuffer* p) { +bool Engine::isValid(const InstanceBuffer* p) const { return downcast(this)->isValid(downcast(p)); } diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index fc26d745fe..7a97aedd7e 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -915,8 +915,9 @@ void FEngine::cleanupResourceListLocked(Lock& lock, ResourceList&& list) { template UTILS_ALWAYS_INLINE -inline bool FEngine::isValid(const T* ptr, ResourceList& list) { - return list.find(ptr) != list.end(); +inline bool FEngine::isValid(const T* ptr, ResourceList const& list) const { + auto& l = const_cast&>(list); + return l.find(ptr) != l.end(); } template @@ -1086,75 +1087,99 @@ void FEngine::destroy(Entity e) { mCameraManager.destroy(*this, e); } -bool FEngine::isValid(const FBufferObject* p) { +bool FEngine::isValid(const FBufferObject* p) const { return isValid(p, mBufferObjects); } -bool FEngine::isValid(const FVertexBuffer* p) { +bool FEngine::isValid(const FVertexBuffer* p) const { return isValid(p, mVertexBuffers); } -bool FEngine::isValid(const FFence* p) { +bool FEngine::isValid(const FFence* p) const { return isValid(p, mFences); } -bool FEngine::isValid(const FIndexBuffer* p) { +bool FEngine::isValid(const FIndexBuffer* p) const { return isValid(p, mIndexBuffers); } -bool FEngine::isValid(const FSkinningBuffer* p) { +bool FEngine::isValid(const FSkinningBuffer* p) const { return isValid(p, mSkinningBuffers); } -bool FEngine::isValid(const FMorphTargetBuffer* p) { +bool FEngine::isValid(const FMorphTargetBuffer* p) const { return isValid(p, mMorphTargetBuffers); } -bool FEngine::isValid(const FIndirectLight* p) { +bool FEngine::isValid(const FIndirectLight* p) const { return isValid(p, mIndirectLights); } -bool FEngine::isValid(const FMaterial* p) { +bool FEngine::isValid(const FMaterial* p) const { return isValid(p, mMaterials); } -bool FEngine::isValid(const FRenderer* p) { +bool FEngine::isValid(const FMaterial* m, const FMaterialInstance* p) const { + // first make sure the material we're given is valid. + if (!isValid(m)) { + return false; + } + + // then find the material instance list for that material + auto it = mMaterialInstances.find(m); + if (it == mMaterialInstances.end()) { + // this could happen if this material has no material instances at all + return false; + } + + // finally validate the material instance + return isValid(p, it->second); +} + +bool FEngine::isValidExpensive(const FMaterialInstance* p) const { + return std::any_of(mMaterialInstances.cbegin(), mMaterialInstances.cend(), + [this, p](auto&& entry) { + return isValid(p, entry.second); + }); +} + +bool FEngine::isValid(const FRenderer* p) const { return isValid(p, mRenderers); } -bool FEngine::isValid(const FScene* p) { +bool FEngine::isValid(const FScene* p) const { return isValid(p, mScenes); } -bool FEngine::isValid(const FSkybox* p) { +bool FEngine::isValid(const FSkybox* p) const { return isValid(p, mSkyboxes); } -bool FEngine::isValid(const FColorGrading* p) { +bool FEngine::isValid(const FColorGrading* p) const { return isValid(p, mColorGradings); } -bool FEngine::isValid(const FSwapChain* p) { +bool FEngine::isValid(const FSwapChain* p) const { return isValid(p, mSwapChains); } -bool FEngine::isValid(const FStream* p) { +bool FEngine::isValid(const FStream* p) const { return isValid(p, mStreams); } -bool FEngine::isValid(const FTexture* p) { +bool FEngine::isValid(const FTexture* p) const { return isValid(p, mTextures); } -bool FEngine::isValid(const FRenderTarget* p) { +bool FEngine::isValid(const FRenderTarget* p) const { return isValid(p, mRenderTargets); } -bool FEngine::isValid(const FView* p) { +bool FEngine::isValid(const FView* p) const { return isValid(p, mViews); } -bool FEngine::isValid(const FInstanceBuffer* p) { +bool FEngine::isValid(const FInstanceBuffer* p) const { return isValid(p, mInstanceBuffers); } diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index b467013b99..c09db0a875 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -318,25 +318,26 @@ public: bool destroy(const FView* p); bool destroy(const FInstanceBuffer* p); - bool isValid(const FBufferObject* p); - bool isValid(const FVertexBuffer* p); - bool isValid(const FFence* p); - bool isValid(const FIndexBuffer* p); - bool isValid(const FSkinningBuffer* p); - bool isValid(const FMorphTargetBuffer* p); - bool isValid(const FIndirectLight* p); - bool isValid(const FMaterial* p); - bool isValid(const FMaterialInstance* p); - bool isValid(const FRenderer* p); - bool isValid(const FScene* p); - bool isValid(const FSkybox* p); - bool isValid(const FColorGrading* p); - bool isValid(const FSwapChain* p); - bool isValid(const FStream* p); - bool isValid(const FTexture* p); - bool isValid(const FRenderTarget* p); - bool isValid(const FView* p); - bool isValid(const FInstanceBuffer* p); + bool isValid(const FBufferObject* p) const; + bool isValid(const FVertexBuffer* p) const; + bool isValid(const FFence* p) const; + bool isValid(const FIndexBuffer* p) const; + bool isValid(const FSkinningBuffer* p) const; + bool isValid(const FMorphTargetBuffer* p) const; + bool isValid(const FIndirectLight* p) const; + bool isValid(const FMaterial* p) const; + bool isValid(const FMaterial* m, const FMaterialInstance* p) const; + bool isValidExpensive(const FMaterialInstance* p) const; + bool isValid(const FRenderer* p) const; + bool isValid(const FScene* p) const; + bool isValid(const FSkybox* p) const; + bool isValid(const FColorGrading* p) const; + bool isValid(const FSwapChain* p) const; + bool isValid(const FStream* p) const; + bool isValid(const FTexture* p) const; + bool isValid(const FRenderTarget* p) const; + bool isValid(const FView* p) const; + bool isValid(const FInstanceBuffer* p) const; void destroy(utils::Entity e); @@ -444,7 +445,7 @@ private: backend::Driver& getDriver() const noexcept { return *mDriver; } template - bool isValid(const T* ptr, ResourceList& list); + bool isValid(const T* ptr, ResourceList const& list) const; template bool terminateAndDestroy(const T* p, ResourceList& list); diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 87da012145..e1a8a89cef 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -576,6 +576,12 @@ class_("Engine") .function("isValidMaterial", EMBIND_LAMBDA(bool, (Engine* engine, Material* object), { return engine->isValid(object); }), allow_raw_pointers()) + .function("isValidMaterialInstance", EMBIND_LAMBDA(bool, (Engine* engine, Material* ma, MaterialInstance* mi), { + return engine->isValid(ma, mi); + }), allow_raw_pointers()) + .function("isValidExpensiveMaterialInstance", EMBIND_LAMBDA(bool, (Engine* engine, MaterialInstance* object), { + return engine->isValidExpensive(object); + }), allow_raw_pointers()) .function("isValidSkybox", EMBIND_LAMBDA(bool, (Engine* engine, Skybox* object), { return engine->isValid(object); }), allow_raw_pointers())