Add a way to query the validity of filament objects

Engine::isValid() can be used to check the validity of most filament
objects.
This commit is contained in:
Mathias Agopian
2023-07-24 15:42:01 -07:00
committed by Mathias Agopian
parent 3dbb7298f8
commit d302525674
10 changed files with 495 additions and 6 deletions

View File

@@ -8,3 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- backend: Disable timer queries on all Mali GPUs (fixes b/233754398)
- engine: Add a way to query the validity of most filament objects (see `Engine::isValid`)
- opengl: fix b/290388359 : possible crash when shutting down the engine

View File

@@ -278,6 +278,112 @@ Java_com_google_android_filament_Engine_nDestroyEntity(JNIEnv*, jclass,
engine->destroy(entity);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidRenderer(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeRenderer) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Renderer*)nativeRenderer);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidView(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeView) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((View*)nativeView);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidScene(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeScene) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Scene*)nativeScene);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidFence(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeFence) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Fence*)nativeFence);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidStream(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeStream) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Stream*)nativeStream);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidIndexBuffer(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeIndexBuffer) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((IndexBuffer*)nativeIndexBuffer);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidVertexBuffer(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeVertexBuffer) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((VertexBuffer*)nativeVertexBuffer);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidSkinningBuffer(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeSkinningBuffer) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((SkinningBuffer*)nativeSkinningBuffer);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidIndirectLight(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeIndirectLight) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((IndirectLight*)nativeIndirectLight);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidMaterial(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeMaterial) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Material*)nativeMaterial);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidSkybox(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeSkybox) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Skybox*)nativeSkybox);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidColorGrading(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeColorGrading) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((ColorGrading*)nativeColorGrading);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidTexture(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeTexture) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((Texture*)nativeTexture);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidRenderTarget(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeTarget) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((RenderTarget*)nativeTarget);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nIsValidSwapChain(JNIEnv*, jclass,
jlong nativeEngine, jlong nativeSwapChain) {
Engine* engine = (Engine *)nativeEngine;
return (jboolean)engine->isValid((SwapChain*)nativeSwapChain);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass,
jlong nativeEngine) {

View File

@@ -449,6 +449,141 @@ public class Engine {
swapChain.clearNativeObject();
}
/**
* Returns whether the object is valid.
* @param object Object to check for validity
* @return returns true if the specified object is valid.
*/
public boolean isValidRenderer(@NonNull Renderer object) {
return nIsValidRenderer(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 isValidView(@NonNull View object) {
return nIsValidView(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 isValidScene(@NonNull Scene object) {
return nIsValidScene(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 isValidFence(@NonNull Fence object) {
return nIsValidFence(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 isValidStream(@NonNull Stream object) {
return nIsValidStream(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 isValidIndexBuffer(@NonNull IndexBuffer object) {
return nIsValidIndexBuffer(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 isValidVertexBuffer(@NonNull VertexBuffer object) {
return nIsValidVertexBuffer(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 isValidSkinningBuffer(@NonNull SkinningBuffer object) {
return nIsValidSkinningBuffer(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 isValidIndirectLight(@NonNull IndirectLight object) {
return nIsValidIndirectLight(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 isValidMaterial(@NonNull Material object) {
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 isValidSkybox(@NonNull Skybox object) {
return nIsValidSkybox(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 isValidColorGrading(@NonNull ColorGrading object) {
return nIsValidColorGrading(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 isValidTexture(@NonNull Texture object) {
return nIsValidTexture(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 isValidRenderTarget(@NonNull RenderTarget object) {
return nIsValidRenderTarget(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 isValidSwapChain(@NonNull SwapChain object) {
return nIsValidSwapChain(getNativeObject(), object.getNativeObject());
}
// View
/**
@@ -785,17 +920,17 @@ public class Engine {
private static native long nCreateSwapChain(long nativeEngine, Object nativeWindow, long flags);
private static native long nCreateSwapChainHeadless(long nativeEngine, int width, int height, long flags);
private static native long nCreateSwapChainFromRawPointer(long nativeEngine, long pointer, long flags);
private static native boolean nDestroySwapChain(long nativeEngine, long nativeSwapChain);
private static native long nCreateView(long nativeEngine);
private static native boolean nDestroyView(long nativeEngine, long nativeView);
private static native long nCreateRenderer(long nativeEngine);
private static native boolean nDestroyRenderer(long nativeEngine, long nativeRenderer);
private static native long nCreateCamera(long nativeEngine, int entity);
private static native long nGetCameraComponent(long nativeEngine, int entity);
private static native void nDestroyCameraComponent(long nativeEngine, int entity);
private static native long nCreateScene(long nativeEngine);
private static native boolean nDestroyScene(long nativeEngine, long nativeScene);
private static native long nCreateFence(long nativeEngine);
private static native boolean nDestroyRenderer(long nativeEngine, long nativeRenderer);
private static native boolean nDestroyView(long nativeEngine, long nativeView);
private static native boolean nDestroyScene(long nativeEngine, long nativeScene);
private static native boolean nDestroyFence(long nativeEngine, long nativeFence);
private static native boolean nDestroyStream(long nativeEngine, long nativeStream);
private static native boolean nDestroyIndexBuffer(long nativeEngine, long nativeIndexBuffer);
@@ -808,6 +943,22 @@ public class Engine {
private static native boolean nDestroyColorGrading(long nativeEngine, long nativeColorGrading);
private static native boolean nDestroyTexture(long nativeEngine, long nativeTexture);
private static native boolean nDestroyRenderTarget(long nativeEngine, long nativeTarget);
private static native boolean nDestroySwapChain(long nativeEngine, long nativeSwapChain);
private static native boolean nIsValidRenderer(long nativeEngine, long nativeRenderer);
private static native boolean nIsValidView(long nativeEngine, long nativeView);
private static native boolean nIsValidScene(long nativeEngine, long nativeScene);
private static native boolean nIsValidFence(long nativeEngine, long nativeFence);
private static native boolean nIsValidStream(long nativeEngine, long nativeStream);
private static native boolean nIsValidIndexBuffer(long nativeEngine, long nativeIndexBuffer);
private static native boolean nIsValidVertexBuffer(long nativeEngine, long nativeVertexBuffer);
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 nIsValidSkybox(long nativeEngine, long nativeSkybox);
private static native boolean nIsValidColorGrading(long nativeEngine, long nativeColorGrading);
private static native boolean nIsValidTexture(long nativeEngine, long nativeTexture);
private static native boolean nIsValidRenderTarget(long nativeEngine, long nativeTarget);
private static native boolean nIsValidSwapChain(long nativeEngine, long nativeSwapChain);
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native long nGetTransformManager(long nativeEngine);

View File

@@ -676,6 +676,25 @@ public:
bool destroy(const InstanceBuffer* p); //!< Destroys an InstanceBuffer object.
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
bool isValid(const BufferObject* p); //!< Tells whether a BufferObject object is valid
bool isValid(const VertexBuffer* p); //!< Tells whether an VertexBuffer object is valid
bool isValid(const Fence* p); //!< Tells whether a Fence object is valid
bool isValid(const IndexBuffer* p); //!< Tells whether an IndexBuffer object is valid
bool isValid(const SkinningBuffer* p); //!< Tells whether a SkinningBuffer object is valid
bool isValid(const MorphTargetBuffer* p); //!< Tells whether a MorphTargetBuffer object is valid
bool isValid(const IndirectLight* p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Material* p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Renderer* p); //!< Tells whether a Renderer object is valid
bool isValid(const Scene* p); //!< Tells whether a Scene object is valid
bool isValid(const Skybox* p); //!< Tells whether a SkyBox object is valid
bool isValid(const ColorGrading* p); //!< Tells whether a ColorGrading object is valid
bool isValid(const SwapChain* p); //!< Tells whether a SwapChain object is valid
bool isValid(const Stream* p); //!< Tells whether a Stream object is valid
bool isValid(const Texture* p); //!< Tells whether a Texture object is valid
bool isValid(const RenderTarget* p); //!< Tells whether a RenderTarget object is valid
bool isValid(const View* p); //!< Tells whether a View object is valid
bool isValid(const InstanceBuffer* p); //!< Tells whether an InstanceBuffer object is valid
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that does guarantee that the

View File

@@ -196,6 +196,61 @@ void Engine::destroy(Entity e) {
downcast(this)->destroy(e);
}
bool Engine::isValid(const BufferObject* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const VertexBuffer* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Fence* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const IndexBuffer* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const SkinningBuffer* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const MorphTargetBuffer* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const IndirectLight* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Material* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Renderer* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Scene* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Skybox* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const ColorGrading* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const SwapChain* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Stream* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Texture* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const RenderTarget* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const View* p) {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const InstanceBuffer* p) {
return downcast(this)->isValid(downcast(p));
}
void Engine::flushAndWait() {
downcast(this)->flushAndWait();
}

View File

@@ -45,12 +45,15 @@ bool ResourceListBase::remove(void const* item) {
return mList.erase(const_cast<void*>(item)) > 0;
}
auto ResourceListBase::find(void const* item) -> iterator {
return mList.find(const_cast<void*>(item));
}
void ResourceListBase::clear() noexcept {
mList.clear();
}
// this is not inlined so we don't pay the code-size cost of iterating the list
// this is not inlined, so we don't pay the code-size cost of iterating the list
void ResourceListBase::forEach(void (* f)(void*, void*), void* user) const noexcept {
std::for_each(mList.begin(), mList.end(), [=](void* p) {
f(user, p);

View File

@@ -38,6 +38,8 @@ public:
bool remove(void const* item);
iterator find(void const* item);
void clear() noexcept;
bool empty() const noexcept {
@@ -76,9 +78,12 @@ public:
using ResourceListBase::forEach;
using ResourceListBase::insert;
using ResourceListBase::remove;
using ResourceListBase::find;
using ResourceListBase::empty;
using ResourceListBase::size;
using ResourceListBase::clear;
using ResourceListBase::begin;
using ResourceListBase::end;
explicit ResourceList(const char* name) noexcept: ResourceListBase(name) {}

View File

@@ -852,6 +852,12 @@ void FEngine::cleanupResourceListLocked(Lock& lock, ResourceList<T>&& list) {
// -----------------------------------------------------------------------------------------------
template<typename T>
UTILS_ALWAYS_INLINE
inline bool FEngine::isValid(const T* ptr, ResourceList<T>& list) {
return list.find(ptr) != list.end();
}
template<typename T>
UTILS_ALWAYS_INLINE
inline bool FEngine::terminateAndDestroy(const T* ptr, ResourceList<T>& list) {
@@ -1019,6 +1025,79 @@ void FEngine::destroy(Entity e) {
mCameraManager.destroy(e);
}
bool FEngine::isValid(const FBufferObject* p) {
return isValid(p, mBufferObjects);
}
bool FEngine::isValid(const FVertexBuffer* p) {
return isValid(p, mVertexBuffers);
}
bool FEngine::isValid(const FFence* p) {
return isValid(p, mFences);
}
bool FEngine::isValid(const FIndexBuffer* p) {
return isValid(p, mIndexBuffers);
}
bool FEngine::isValid(const FSkinningBuffer* p) {
return isValid(p, mSkinningBuffers);
}
bool FEngine::isValid(const FMorphTargetBuffer* p) {
return isValid(p, mMorphTargetBuffers);
}
bool FEngine::isValid(const FIndirectLight* p) {
return isValid(p, mIndirectLights);
}
bool FEngine::isValid(const FMaterial* p) {
return isValid(p, mMaterials);
}
bool FEngine::isValid(const FRenderer* p) {
return isValid(p, mRenderers);
}
bool FEngine::isValid(const FScene* p) {
return isValid(p, mScenes);
}
bool FEngine::isValid(const FSkybox* p) {
return isValid(p, mSkyboxes);
}
bool FEngine::isValid(const FColorGrading* p) {
return isValid(p, mColorGradings);
}
bool FEngine::isValid(const FSwapChain* p) {
return isValid(p, mSwapChains);
}
bool FEngine::isValid(const FStream* p) {
return isValid(p, mStreams);
}
bool FEngine::isValid(const FTexture* p) {
return isValid(p, mTextures);
}
bool FEngine::isValid(const FRenderTarget* p) {
return isValid(p, mRenderTargets);
}
bool FEngine::isValid(const FView* p) {
return isValid(p, mViews);
}
bool FEngine::isValid(const FInstanceBuffer* p) {
return isValid(p, mInstanceBuffers);
}
void* FEngine::streamAlloc(size_t size, size_t alignment) noexcept {
// we allow this only for small allocations
if (size > 65536) {

View File

@@ -293,6 +293,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);
void destroy(utils::Entity e);
void flushAndWait();
@@ -392,6 +412,9 @@ private:
backend::Driver& getDriver() const noexcept { return *mDriver; }
template<typename T>
bool isValid(const T* ptr, ResourceList<T>& list);
template<typename T>
bool terminateAndDestroy(const T* p, ResourceList<T>& list);

View File

@@ -540,7 +540,53 @@ class_<Engine>("Engine")
/// vb ::argument:: the [VertexBuffer] to destroy
.function("destroyVertexBuffer", (void (*)(Engine*, VertexBuffer*)) []
(Engine* engine, VertexBuffer* vb) { engine->destroy(vb); },
allow_raw_pointers());
allow_raw_pointers())
.function("isValidRenderer", EMBIND_LAMBDA(bool, (Engine* engine, Renderer* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidView", EMBIND_LAMBDA(bool, (Engine* engine, View* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidScene", EMBIND_LAMBDA(bool, (Engine* engine, Scene* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidFence", EMBIND_LAMBDA(bool, (Engine* engine, Fence* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidStream", EMBIND_LAMBDA(bool, (Engine* engine, Stream* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidIndexBuffer", EMBIND_LAMBDA(bool, (Engine* engine, IndexBuffer* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidVertexBuffer", EMBIND_LAMBDA(bool, (Engine* engine, VertexBuffer* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidSkinningBuffer", EMBIND_LAMBDA(bool, (Engine* engine, SkinningBuffer* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidIndirectLight", EMBIND_LAMBDA(bool, (Engine* engine, IndirectLight* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidMaterial", EMBIND_LAMBDA(bool, (Engine* engine, Material* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidSkybox", EMBIND_LAMBDA(bool, (Engine* engine, Skybox* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidColorGrading", EMBIND_LAMBDA(bool, (Engine* engine, ColorGrading* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidTexture", EMBIND_LAMBDA(bool, (Engine* engine, Texture* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidRenderTarget", EMBIND_LAMBDA(bool, (Engine* engine, RenderTarget* object), {
return engine->isValid(object);
}), allow_raw_pointers())
.function("isValidSwapChain", EMBIND_LAMBDA(bool, (Engine* engine, SwapChain* object), {
return engine->isValid(object);
}), allow_raw_pointers());
/// SwapChain ::core class:: Represents the platform's native rendering surface.
/// See also the [Engine] methods `createSwapChain` and `destroySwapChain`.