backend: Propagate backend thread exceptions to the main thread. (#9903)

* Filament Error Handling Remediation and noexcept Cleanup

- Replaced assert_invariant with FILAMENT_CHECK_PRECONDITION in
  Renderer::beginFrame for caller bugs.
- Removed noexcept from Camera::setCustomProjection to allow precondition
  checks to throw.
- Removed noexcept from Texture and InstanceBuffer methods using precondition
  checks.
- Documented silent clamping in View::setBloomOptions.
- Clarified comment in FrameGraphResources regarding preconditions.
- Refined MaterialInstance::commit to split texture loop into check and update
  passes, preventing partial state changes on precondition failure.

* backend: Propagate backend thread exceptions to the main thread.

Introduce a mechanism to catch exceptions thrown on the backend thread and
rethrow them on the main thread. This prevents deadlocks and allows the
application to handle fatal backend failures gracefully.

- Consolidate synchronization primitives in DriverBase.
- Add mHasUnrecoverableError flag to interrupt blocked fence waits.
- Optimize waitForFence to avoid extra atomic reads in common paths.
- Propagate FenceStatus::ERROR across all backends.
- CommandBufferQueue now stores a std::exception_ptr when the backend fails.
- The backend enters a "zombie" state on failure, skipping further command
  execution but allowing clean shutdown.
- Public APIs in Renderer and Engine now check for stored exceptions and
  rethrow them, documented with @throws.
- Guarded by __EXCEPTIONS to ensure no overhead when exceptions are disabled.
- Add unit test for fence interruption and document new APIs.

BUGS=[407545700]

* feat: harden backend exceptions and add hasUnrecoverableFailure API

- Update Renderer::beginFrame() and Renderer::shouldRenderFrame() to
  return false early if an unrecoverable backend exception has been
  delivered to the main thread.
- Document the new return behavior for beginFrame() and
  shouldRenderFrame() in Renderer.h.
- Add Engine::hasUnrecoverableFailure() to the public API to allow
  apps to check for fatal errors without relying on exceptions.
- Implement hasUnrecoverableFailure() in FEngine by delegating to
  CommandBufferQueue.
- Expose Engine::hasUnrecoverableFailure() to Java bindings
  (Engine.java and JNI).
- Expose Engine::hasUnrecoverableFailure() to JavaScript bindings
  (jsbindings.cpp).

* java: propagate C++ Panics & exceptions to JAVA
This commit is contained in:
Mathias Agopian
2026-04-22 10:56:09 -07:00
committed by GitHub
parent fa987f225d
commit f4a079f663
81 changed files with 1381 additions and 525 deletions
+11
View File
@@ -75,6 +75,17 @@ void JniCallback::postToJavaAndDestroy(JniCallback* callback) {
delete callback; delete callback;
} }
void JniCallback::destroy(JniCallback* callback) {
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
env->DeleteGlobalRef(callback->mHandler);
env->DeleteGlobalRef(callback->mCallback);
#ifdef __ANDROID__
env->DeleteGlobalRef(callback->mCallbackUtils.handlerClass);
#endif
env->DeleteGlobalRef(callback->mCallbackUtils.executorClass);
delete callback;
}
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
JniBufferCallback* JniBufferCallback::make(filament::Engine*, JniBufferCallback* JniBufferCallback::make(filament::Engine*,
+3
View File
@@ -48,6 +48,9 @@ struct JniCallback : private filament::backend::CallbackHandler {
// execute the callback on the java thread and destroy ourselves // execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(JniCallback* callback); static void postToJavaAndDestroy(JniCallback* callback);
// destroy ourselves without executing the callback
static void destroy(JniCallback* callback);
// CallbackHandler interface. // CallbackHandler interface.
void post(void* user, Callback callback) override; void post(void* user, Callback callback) override;
+51
View File
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JniUtils.h"
namespace filament {
namespace android {
#ifdef __EXCEPTIONS
UTILS_NOINLINE void wrapJniHelper(JNIEnv* env, void (*invoker)(void*), void* userData) {
try {
invoker(userData);
} catch (const utils::PreconditionPanic& e) {
jclass exClass = env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(exClass, e.what());
} catch (const utils::PostconditionPanic& e) {
jclass exClass = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(exClass, e.what());
} catch (const std::exception& e) {
jclass exClass = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(exClass, e.what());
}
}
UTILS_NOINLINE void wrapJniBackendHelper(JNIEnv* env, void (*invoker)(void*), void* userData) {
try {
invoker(userData);
} catch (const std::exception& e) {
jclass exClass = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(exClass, e.what());
}
}
#endif
} // namespace android
} // namespace filament
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_ANDROID_COMMON_JNIUTILS_H
#define TNT_ANDROID_COMMON_JNIUTILS_H
#include <jni.h>
#include <exception>
#include <type_traits>
#include <utils/Panic.h>
#include <utils/compiler.h>
namespace filament {
namespace android {
#ifdef __EXCEPTIONS
// Non-templated helpers implemented in JniUtils.cpp
void wrapJniHelper(JNIEnv* env, void (*invoker)(void*), void* userData);
void wrapJniBackendHelper(JNIEnv* env, void (*invoker)(void*), void* userData);
// For JNI methods that return a value
template<typename R, typename F>
R wrapJni(JNIEnv* env, F const& f) {
if constexpr (std::is_void_v<R>) {
auto invoker = [](void* data) {
auto& f_ref = *reinterpret_cast<const F*>(data);
f_ref();
};
wrapJniHelper(env, invoker, (void*)&f);
} else {
struct Context {
const F& f;
R result;
};
Context ctx{ f, R{} };
auto invoker = [](void* data) {
auto& context = *reinterpret_cast<Context*>(data);
context.result = context.f();
};
wrapJniHelper(env, invoker, &ctx);
return ctx.result;
}
}
// Overload for JNI methods that return void
template<typename F>
inline void wrapJni(JNIEnv* env, F const& f) {
wrapJni<void, F>(env, f);
}
// For JNI methods that can return backend errors (mapped to java.lang.Error)
template<typename R, typename F>
R wrapJniBackend(JNIEnv* env, F const& f) {
if constexpr (std::is_void_v<R>) {
auto invoker = [](void* data) {
auto& f_ref = *reinterpret_cast<const F*>(data);
f_ref();
};
wrapJniBackendHelper(env, invoker, (void*)&f);
} else {
struct Context {
const F& f;
R result;
};
Context ctx{ f, R{} };
auto invoker = [](void* data) {
auto& context = *reinterpret_cast<Context*>(data);
context.result = context.f();
};
wrapJniBackendHelper(env, invoker, &ctx);
return ctx.result;
}
}
template<typename F>
inline void wrapJniBackend(JNIEnv* env, F const& f) {
wrapJniBackend<void, F>(env, f);
}
#else
// For JNI methods that return a value
template<typename R, typename F>
R wrapJni(JNIEnv* env, F const& f) {
return f();
}
// Overload for JNI methods that return void
template<typename F>
inline void wrapJni(JNIEnv* env, F const& f) {
f();
}
template<typename R, typename F>
R wrapJniBackend(JNIEnv* env, F const& f) {
return f();
}
template<typename F>
inline void wrapJniBackend(JNIEnv* env, F const& f) {
f();
}
#endif
} // namespace android
} // namespace filament
#endif // TNT_ANDROID_COMMON_JNIUTILS_H
+1
View File
@@ -126,6 +126,7 @@ add_library(filament-jni SHARED
# Common utils # Common utils
../common/CallbackUtils.cpp ../common/CallbackUtils.cpp
../common/NioUtils.cpp ../common/NioUtils.cpp
../common/JniUtils.cpp
) )
target_include_directories(filament-jni PRIVATE target_include_directories(filament-jni PRIVATE
@@ -16,7 +16,6 @@
#include <jni.h> #include <jni.h>
#include <functional>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -26,6 +25,7 @@
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
@@ -63,7 +63,9 @@ Java_com_google_android_filament_BufferObject_nBuilderBuild(JNIEnv *env, jclass
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder; BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -81,20 +83,22 @@ Java_com_google_android_filament_BufferObject_nSetBuffer(JNIEnv *env, jclass typ
BufferObject *bufferObject = (BufferObject *) nativeBufferObject; BufferObject *bufferObject = (BufferObject *) nativeBufferObject;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, buffer, count); return filament::android::wrapJni<int>(env, [=]() {
void* data = nioBuffer.getData(); AutoBuffer nioBuffer(env, buffer, count);
size_t sizeInBytes = nioBuffer.getSize(); void* data = nioBuffer.getData();
if (sizeInBytes > (remaining << nioBuffer.getShift())) { size_t sizeInBytes = nioBuffer.getSize();
// BufferOverflowException if (sizeInBytes > (remaining << nioBuffer.getShift())) {
return -1; // BufferOverflowException
} return -1;
}
auto* callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer)); auto* callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
BufferDescriptor desc(data, sizeInBytes, BufferDescriptor desc(data, sizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
bufferObject->setBuffer(*engine, std::move(desc), (uint32_t) destOffsetInBytes); bufferObject->setBuffer(*engine, std::move(desc), (uint32_t) destOffsetInBytes);
return 0; return 0;
});
} }
@@ -17,6 +17,7 @@
#include <jni.h> #include <jni.h>
#include <filament/Camera.h> #include <filament/Camera.h>
#include <common/JniUtils.h>
#include <utils/Entity.h> #include <utils/Entity.h>
@@ -24,21 +25,26 @@
#include <math/mat4.h> #include <math/mat4.h>
using namespace filament; using namespace filament;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Camera_nSetProjection(JNIEnv*, jclass, jlong nativeCamera, Java_com_google_android_filament_Camera_nSetProjection(JNIEnv* env, jclass, jlong nativeCamera,
jint projection, jdouble left, jdouble right, jdouble bottom, jdouble top, jdouble near, jint projection, jdouble left, jdouble right, jdouble bottom, jdouble top, jdouble near,
jdouble far) { jdouble far) {
Camera *camera = (Camera *) nativeCamera; Camera *camera = (Camera *) nativeCamera;
camera->setProjection((Camera::Projection) projection, left, right, bottom, top, near, far); wrapJni(env, [=]() {
camera->setProjection((Camera::Projection) projection, left, right, bottom, top, near, far);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Camera_nSetProjectionFov(JNIEnv*, jclass , Java_com_google_android_filament_Camera_nSetProjectionFov(JNIEnv* env, jclass ,
jlong nativeCamera, jdouble fovInDegrees, jdouble aspect, jdouble near, jdouble far, jlong nativeCamera, jdouble fovInDegrees, jdouble aspect, jdouble near, jdouble far,
jint fov) { jint fov) {
Camera *camera = (Camera *) nativeCamera; Camera *camera = (Camera *) nativeCamera;
camera->setProjection(fovInDegrees, aspect, near, far, (Camera::Fov) fov); wrapJni(env, [=]() {
camera->setProjection(fovInDegrees, aspect, near, far, (Camera::Fov) fov);
});
} }
extern "C" JNIEXPORT jdouble JNICALL extern "C" JNIEXPORT jdouble JNICALL
@@ -49,10 +55,12 @@ Java_com_google_android_filament_Camera_nGetFieldOfViewInDegrees(JNIEnv*, jclass
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Camera_nSetLensProjection(JNIEnv*, jclass, Java_com_google_android_filament_Camera_nSetLensProjection(JNIEnv* env, jclass,
jlong nativeCamera, jdouble focalLength, jdouble aspect, jdouble near, jdouble far) { jlong nativeCamera, jdouble focalLength, jdouble aspect, jdouble near, jdouble far) {
Camera *camera = (Camera *) nativeCamera; Camera *camera = (Camera *) nativeCamera;
camera->setLensProjection(focalLength, aspect, near, far); wrapJni(env, [=]() {
camera->setLensProjection(focalLength, aspect, near, far);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -62,10 +70,12 @@ Java_com_google_android_filament_Camera_nSetCustomProjection(JNIEnv *env, jclass
Camera *camera = (Camera *) nativeCamera; Camera *camera = (Camera *) nativeCamera;
jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL); jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL);
jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL); jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL);
camera->setCustomProjection( wrapJni(env, [=]() {
*reinterpret_cast<const filament::math::mat4 *>(inProjection), camera->setCustomProjection(
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling), *reinterpret_cast<const filament::math::mat4 *>(inProjection),
near, far); *reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
near, far);
});
env->ReleaseDoubleArrayElements(inProjection_, inProjection, JNI_ABORT); env->ReleaseDoubleArrayElements(inProjection_, inProjection, JNI_ABORT);
env->ReleaseDoubleArrayElements(inProjectionForCulling_, inProjectionForCulling, JNI_ABORT); env->ReleaseDoubleArrayElements(inProjectionForCulling_, inProjectionForCulling, JNI_ABORT);
} }
@@ -77,10 +87,12 @@ Java_com_google_android_filament_Camera_nSetCustomEyeProjection(JNIEnv *env, jcl
Camera *camera = (Camera *) nativeCamera; Camera *camera = (Camera *) nativeCamera;
jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL); jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL);
jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL); jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL);
camera->setCustomEyeProjection( wrapJni(env, [=]() {
reinterpret_cast<const filament::math::mat4 *>(inProjection), (size_t) count, camera->setCustomEyeProjection(
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling), reinterpret_cast<const filament::math::mat4 *>(inProjection), (size_t) count,
near, far); *reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
near, far);
});
env->ReleaseDoubleArrayElements(inProjection_, inProjection, JNI_ABORT); env->ReleaseDoubleArrayElements(inProjection_, inProjection, JNI_ABORT);
env->ReleaseDoubleArrayElements(inProjectionForCulling_, inProjectionForCulling, JNI_ABORT); env->ReleaseDoubleArrayElements(inProjectionForCulling_, inProjectionForCulling, JNI_ABORT);
} }
@@ -154,7 +166,9 @@ Java_com_google_android_filament_Camera_nSetEyeModelMatrix(JNIEnv *env, jclass,
jlong nativeCamera, jint eyeId, jdoubleArray model_) { jlong nativeCamera, jint eyeId, jdoubleArray model_) {
Camera* camera = (Camera *) nativeCamera; Camera* camera = (Camera *) nativeCamera;
jdouble *model = env->GetDoubleArrayElements(model_, NULL); jdouble *model = env->GetDoubleArrayElements(model_, NULL);
camera->setEyeModelMatrix((uint8_t)eyeId, *reinterpret_cast<const filament::math::mat4*>(model)); wrapJni(env, [=]() {
camera->setEyeModelMatrix((uint8_t)eyeId, *reinterpret_cast<const filament::math::mat4 *>(model));
});
env->ReleaseDoubleArrayElements(model_, model, JNI_ABORT); env->ReleaseDoubleArrayElements(model_, model, JNI_ABORT);
} }
@@ -21,6 +21,7 @@
#include <math/vec3.h> #include <math/vec3.h>
#include <math/vec4.h> #include <math/vec4.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace math; using namespace math;
@@ -37,10 +38,12 @@ Java_com_google_android_filament_ColorGrading_nDestroyBuilder(JNIEnv*, jclass, j
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ColorGrading_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jlong nativeEngine) { Java_com_google_android_filament_ColorGrading_nBuilderBuild(JNIEnv* env, jclass, jlong nativeBuilder, jlong nativeEngine) {
ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder; ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
+131 -62
View File
@@ -16,6 +16,8 @@
#include <jni.h> #include <jni.h>
#include <exception>
#include <filament/Camera.h> #include <filament/Camera.h>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/MorphTargetBuffer.h> #include <filament/MorphTargetBuffer.h>
@@ -28,14 +30,18 @@
#include <filament/View.h> #include <filament/View.h>
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/JniUtils.h"
using namespace filament; using namespace filament;
using namespace utils; using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass, jlong nativeEngine) { Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv *env, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine; wrapJni(env, [=]() {
Engine::destroy(&engine); Engine* engine = (Engine*) nativeEngine;
Engine::destroy(&engine);
});
} }
// SwapChain // SwapChain
@@ -77,11 +83,13 @@ Java_com_google_android_filament_Engine_nCreateSwapChainFromRawPointer(JNIEnv*,
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroySwapChain(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroySwapChain(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeSwapChain) { jlong nativeEngine, jlong nativeSwapChain) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
SwapChain* swapChain = (SwapChain*) nativeSwapChain; SwapChain* swapChain = (SwapChain*) nativeSwapChain;
return engine->destroy(swapChain); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(swapChain);
});
} }
// View // View
@@ -94,11 +102,13 @@ Java_com_google_android_filament_Engine_nCreateView(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyView(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyView(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeView) { jlong nativeEngine, jlong nativeView) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
View* view = (View*) nativeView; View* view = (View*) nativeView;
return engine->destroy(view); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(view);
});
} }
// Renderer // Renderer
@@ -111,11 +121,13 @@ Java_com_google_android_filament_Engine_nCreateRenderer(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyRenderer(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyRenderer(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeRenderer) { jlong nativeEngine, jlong nativeRenderer) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Renderer* renderer = (Renderer*) nativeRenderer; Renderer* renderer = (Renderer*) nativeRenderer;
return engine->destroy(renderer); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(renderer);
});
} }
// Camera // Camera
@@ -137,11 +149,13 @@ Java_com_google_android_filament_Engine_nGetCameraComponent(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nDestroyCameraComponent(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyCameraComponent(JNIEnv *env, jclass,
jlong nativeEngine, jint entity_) { jlong nativeEngine, jint entity_) {
Engine* engine = (Engine*) nativeEngine; wrapJni(env, [=]() {
Entity& entity = *reinterpret_cast<Entity*>(&entity_); Engine* engine = (Engine*) nativeEngine;
engine->destroyCameraComponent(entity); Entity entity = *reinterpret_cast<const Entity*>(&entity_);
engine->destroyCameraComponent(entity);
});
} }
// Scene // Scene
@@ -154,11 +168,13 @@ Java_com_google_android_filament_Engine_nCreateScene(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyScene(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyScene(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeScene) { jlong nativeEngine, jlong nativeScene) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Scene* scene = (Scene*) nativeScene; Scene* scene = (Scene*) nativeScene;
return engine->destroy(scene); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(scene);
});
} }
// Fence // Fence
@@ -171,119 +187,147 @@ Java_com_google_android_filament_Engine_nCreateFence(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyFence(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyFence(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeFence) { jlong nativeEngine, jlong nativeFence) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Fence* fence = (Fence*) nativeFence; Fence* fence = (Fence*) nativeFence;
return engine->destroy(fence); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(fence);
});
} }
// Stream // Stream
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyStream(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyStream(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeStream) { jlong nativeEngine, jlong nativeStream) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Stream* stream = (Stream*) nativeStream; Stream* stream = (Stream*) nativeStream;
return engine->destroy(stream); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(stream);
});
} }
// Others... // Others...
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyIndexBuffer(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyIndexBuffer(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeIndexBuffer) { jlong nativeEngine, jlong nativeIndexBuffer) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
IndexBuffer* indexBuffer = (IndexBuffer*) nativeIndexBuffer; IndexBuffer* indexBuffer = (IndexBuffer*) nativeIndexBuffer;
return engine->destroy(indexBuffer); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(indexBuffer);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyVertexBuffer(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyVertexBuffer(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeVertexBuffer) { jlong nativeEngine, jlong nativeVertexBuffer) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
VertexBuffer* vertexBuffer = (VertexBuffer*) nativeVertexBuffer; VertexBuffer* vertexBuffer = (VertexBuffer*) nativeVertexBuffer;
return engine->destroy(vertexBuffer); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(vertexBuffer);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroySkinningBuffer(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroySkinningBuffer(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeSkinningBuffer) { jlong nativeEngine, jlong nativeSkinningBuffer) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
SkinningBuffer* skinningBuffer = (SkinningBuffer*) nativeSkinningBuffer; SkinningBuffer* skinningBuffer = (SkinningBuffer*) nativeSkinningBuffer;
return engine->destroy(skinningBuffer); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(skinningBuffer);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyMorphTargetBuffer(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyMorphTargetBuffer(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeMorphTargetBuffer) { jlong nativeEngine, jlong nativeMorphTargetBuffer) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
MorphTargetBuffer* mtb = (MorphTargetBuffer*) nativeMorphTargetBuffer; MorphTargetBuffer* mtb = (MorphTargetBuffer*) nativeMorphTargetBuffer;
return engine->destroy(mtb); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(mtb);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyIndirectLight(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyIndirectLight(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeIndirectLight) { jlong nativeEngine, jlong nativeIndirectLight) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
IndirectLight* indirectLight = (IndirectLight*) nativeIndirectLight; IndirectLight* indirectLight = (IndirectLight*) nativeIndirectLight;
return engine->destroy(indirectLight); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(indirectLight);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyMaterial(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyMaterial(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeMaterial) { jlong nativeEngine, jlong nativeMaterial) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Material* material = (Material*) nativeMaterial; Material* material = (Material*) nativeMaterial;
return engine->destroy(material); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(material);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyMaterialInstance(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyMaterialInstance(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeMaterialInstance) { jlong nativeEngine, jlong nativeMaterialInstance) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
MaterialInstance* materialInstance = (MaterialInstance*) nativeMaterialInstance; MaterialInstance* materialInstance = (MaterialInstance*) nativeMaterialInstance;
return engine->destroy(materialInstance); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(materialInstance);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroySkybox(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroySkybox(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeSkybox) { jlong nativeEngine, jlong nativeSkybox) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Skybox* skybox = (Skybox*) nativeSkybox; Skybox* skybox = (Skybox*) nativeSkybox;
return engine->destroy(skybox); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(skybox);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyColorGrading(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyColorGrading(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeColorGrading) { jlong nativeEngine, jlong nativeColorGrading) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
ColorGrading* colorGrading = (ColorGrading*) nativeColorGrading; ColorGrading* colorGrading = (ColorGrading*) nativeColorGrading;
return engine->destroy(colorGrading); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(colorGrading);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyTexture(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyTexture(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeTexture) { jlong nativeEngine, jlong nativeTexture) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Texture* texture = (Texture*) nativeTexture; Texture* texture = (Texture*) nativeTexture;
return engine->destroy(texture); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(texture);
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nDestroyRenderTarget(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyRenderTarget(JNIEnv *env, jclass,
jlong nativeEngine, jlong nativeTarget) { jlong nativeEngine, jlong nativeTarget) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
RenderTarget* target = (RenderTarget*) nativeTarget; RenderTarget* target = (RenderTarget*) nativeTarget;
return engine->destroy(target); return wrapJni<jboolean>(env, [=]() {
return engine->destroy(target);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nDestroyEntity(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nDestroyEntity(JNIEnv *env, jclass,
jlong nativeEngine, jint entity_) { jlong nativeEngine, jint entity_) {
Engine* engine = (Engine*) nativeEngine; wrapJni(env, [=]() {
Entity& entity = *reinterpret_cast<Entity*>(&entity_); Engine* engine = (Engine*) nativeEngine;
engine->destroy(entity); Entity entity = *reinterpret_cast<const Entity*>(&entity_);
engine->destroy(entity);
});
} }
@@ -415,17 +459,21 @@ Java_com_google_android_filament_Engine_nIsValidSwapChain(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv *env, jclass,
jlong nativeEngine, jlong timeout) { jlong nativeEngine, jlong timeout) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
return engine->flushAndWait((uint64_t)timeout); return wrapJni<jboolean>(env, [=]() {
return engine->flushAndWait((uint64_t)timeout);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass, Java_com_google_android_filament_Engine_nFlush(JNIEnv *env, jclass,
jlong nativeEngine) { jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
engine->flush(); wrapJni(env, [=]() {
engine->flush();
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
@@ -436,21 +484,32 @@ Java_com_google_android_filament_Engine_nIsPaused(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nCompile(JNIEnv* env, jclass, Java_com_google_android_filament_Engine_nCompile(JNIEnv *env, jclass,
jlong nativeEngine, jint priority, jlong nativeMaterial, jlong nativeView, jlong nativeEngine, jint priority, jlong nativeMaterial, jlong nativeView,
jint shadowReceiver, jint skinning, jobject handler, jobject runnable) { jint shadowReceiver, jint skinning, jobject handler, jobject runnable) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
Material* material = (Material*) nativeMaterial; Material* material = (Material*) nativeMaterial;
View* view = (View*) nativeView; View* view = (View*) nativeView;
JniCallback* jniCallback = JniCallback::make(env, handler, runnable); JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
engine->compile( wrapJni(env, [=]() {
(backend::CompilerPriorityQueue) priority, #if defined(__EXCEPTIONS)
material, view, try {
(utils::tribool::value_t) shadowReceiver, #endif
(utils::tribool::value_t) skinning, engine->compile(
jniCallback->getHandler(), [jniCallback](Material*){ (backend::CompilerPriorityQueue) priority,
JniCallback::postToJavaAndDestroy(jniCallback); material, view,
}); (utils::tribool::value_t) shadowReceiver,
(utils::tribool::value_t) skinning,
jniCallback->getHandler(), [jniCallback](Material*){
JniCallback::postToJavaAndDestroy(jniCallback);
});
#if defined(__EXCEPTIONS)
} catch (...) {
JniCallback::destroy(jniCallback);
throw;
}
#endif
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -511,6 +570,12 @@ Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, j
return (jboolean)engine->isAutomaticInstancingEnabled(); return (jboolean)engine->isAutomaticInstancingEnabled();
} }
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nHasUnrecoverableFailure(JNIEnv*, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jboolean)engine->hasUnrecoverableFailure();
}
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nGetMaxStereoscopicEyes(JNIEnv*, jclass, jlong nativeEngine) { Java_com_google_android_filament_Engine_nGetMaxStereoscopicEyes(JNIEnv*, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
@@ -526,10 +591,12 @@ Java_com_google_android_filament_Engine_nGetSupportedFeatureLevel(JNIEnv *, jcla
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Engine_nSetActiveFeatureLevel(JNIEnv *, jclass, Java_com_google_android_filament_Engine_nSetActiveFeatureLevel(JNIEnv *env, jclass,
jlong nativeEngine, jint ordinal) { jlong nativeEngine, jint ordinal) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
return (jint)engine->setActiveFeatureLevel((Engine::FeatureLevel)ordinal); return wrapJni<jint>(env, [=]() {
return (jint)engine->setActiveFeatureLevel((Engine::FeatureLevel)ordinal);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -651,9 +718,11 @@ Java_com_google_android_filament_Engine_nSetBuilderFeature(JNIEnv *env, jclass c
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) { Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv *env, jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder; Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
return (jlong) builder->build(); return wrapJniBackend<jlong>(env, [=]() {
return (jlong) builder->build();
});
} }
extern "C" extern "C"
@@ -17,6 +17,7 @@
#include <jni.h> #include <jni.h>
#include <filament/Fence.h> #include <filament/Fence.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
@@ -24,13 +25,17 @@ extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Fence_nWait(JNIEnv *env, jclass type, jlong nativeFence, jint mode, Java_com_google_android_filament_Fence_nWait(JNIEnv *env, jclass type, jlong nativeFence, jint mode,
jlong timeoutNanoSeconds) { jlong timeoutNanoSeconds) {
Fence *fence = (Fence *) nativeFence; Fence *fence = (Fence *) nativeFence;
return (jint) fence->wait((Fence::Mode) mode, (uint64_t) timeoutNanoSeconds); return filament::android::wrapJniBackend<jint>(env, [=]() {
return (jint) fence->wait((Fence::Mode) mode, (uint64_t) timeoutNanoSeconds);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Fence_nWaitAndDestroy(JNIEnv *env, jclass type, jlong nativeFence, Java_com_google_android_filament_Fence_nWaitAndDestroy(JNIEnv *env, jclass type, jlong nativeFence,
jint mode) { jint mode) {
Fence *fence = (Fence *) nativeFence; Fence *fence = (Fence *) nativeFence;
return (jint) Fence::waitAndDestroy(fence, (Fence::Mode) mode); return filament::android::wrapJniBackend<jint>(env, [=]() {
return (jint) Fence::waitAndDestroy(fence, (Fence::Mode) mode);
});
} }
@@ -16,11 +16,11 @@
#include <jni.h> #include <jni.h>
#include <functional>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <filament/IndexBuffer.h> #include <filament/IndexBuffer.h>
#include <common/JniUtils.h>
#include <backend/BufferDescriptor.h> #include <backend/BufferDescriptor.h>
@@ -29,6 +29,7 @@
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
using namespace filament::android;
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_IndexBuffer_nCreateBuilder(JNIEnv *env, jclass type) { Java_com_google_android_filament_IndexBuffer_nCreateBuilder(JNIEnv *env, jclass type) {
@@ -63,7 +64,9 @@ Java_com_google_android_filament_IndexBuffer_nBuilderBuild(JNIEnv *env, jclass t
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
IndexBuffer::Builder* builder = (IndexBuffer::Builder *) nativeBuilder; IndexBuffer::Builder* builder = (IndexBuffer::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -94,7 +97,9 @@ Java_com_google_android_filament_IndexBuffer_nSetBuffer(JNIEnv *env, jclass type
BufferDescriptor desc(data, sizeInBytes, BufferDescriptor desc(data, sizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
indexBuffer->setBuffer(*engine, std::move(desc), (uint32_t) destOffsetInBytes); wrapJni(env, [&]() {
indexBuffer->setBuffer(*engine, std::move(desc), (uint32_t) destOffsetInBytes);
});
return 0; return 0;
} }
@@ -21,6 +21,7 @@
#include <common/NioUtils.h> #include <common/NioUtils.h>
#include <common/CallbackUtils.h> #include <common/CallbackUtils.h>
#include <math/mat4.h> #include <math/mat4.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
@@ -37,11 +38,13 @@ Java_com_google_android_filament_IndirectLight_nDestroyBuilder(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_IndirectLight_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_IndirectLight_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
IndirectLight::Builder* builder = (IndirectLight::Builder*) nativeBuilder; IndirectLight::Builder* builder = (IndirectLight::Builder*) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -17,6 +17,7 @@
#include <jni.h> #include <jni.h>
#include <filament/LightManager.h> #include <filament/LightManager.h>
#include <common/JniUtils.h>
#include <utils/Entity.h> #include <utils/Entity.h>
@@ -24,6 +25,7 @@
using namespace filament; using namespace filament;
using namespace utils; using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_LightManager_nGetComponentCount(JNIEnv*, jclass, Java_com_google_android_filament_LightManager_nGetComponentCount(JNIEnv*, jclass,
@@ -210,11 +212,13 @@ Java_com_google_android_filament_LightManager_nBuilderLightChannel(JNIEnv*, jcla
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_LightManager_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_LightManager_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder, jlong nativeEngine, jint entity) { jlong nativeBuilder, jlong nativeEngine, jint entity) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return jboolean(builder->build(*engine, (Entity &) entity) == LightManager::Builder::Success); return wrapJni<jboolean>(env, [=]() {
return jboolean(builder->build(*engine, (Entity &) entity) == LightManager::Builder::Success);
});
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@@ -223,7 +227,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nComputeUniformSplits(JNIEnv* env, jclass, Java_com_google_android_filament_LightManager_nComputeUniformSplits(JNIEnv* env, jclass,
jfloatArray splitPositions, jint cascades) { jfloatArray splitPositions, jint cascades) {
jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL); jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL);
LightManager::ShadowCascades::computeUniformSplits(nativeSplits, (uint8_t) cascades); wrapJni(env, [=]() {
LightManager::ShadowCascades::computeUniformSplits(nativeSplits, (uint8_t) cascades);
});
env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0); env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0);
} }
@@ -231,7 +237,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nComputeLogSplits(JNIEnv* env, jclass, Java_com_google_android_filament_LightManager_nComputeLogSplits(JNIEnv* env, jclass,
jfloatArray splitPositions, jint cascades, jfloat near, jfloat far) { jfloatArray splitPositions, jint cascades, jfloat near, jfloat far) {
jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL); jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL);
LightManager::ShadowCascades::computeLogSplits(nativeSplits, (uint8_t) cascades, near, far); wrapJni(env, [=]() {
LightManager::ShadowCascades::computeLogSplits(nativeSplits, (uint8_t) cascades, near, far);
});
env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0); env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0);
} }
@@ -239,7 +247,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nComputePracticalSplits(JNIEnv* env, jclass, Java_com_google_android_filament_LightManager_nComputePracticalSplits(JNIEnv* env, jclass,
jfloatArray splitPositions, jint cascades, jfloat near, jfloat far, jfloat lambda) { jfloatArray splitPositions, jint cascades, jfloat near, jfloat far, jfloat lambda) {
jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL); jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL);
LightManager::ShadowCascades::computePracticalSplits(nativeSplits, (uint8_t) cascades, near, far, lambda); wrapJni(env, [=]() {
LightManager::ShadowCascades::computePracticalSplits(nativeSplits, (uint8_t) cascades, near, far, lambda);
});
env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0); env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0);
} }
@@ -20,25 +20,28 @@
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace filament::android;
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass, Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) { jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, buffer_, size); return wrapJni<jlong>(env, [=]() {
auto builder = Material::Builder(); AutoBuffer buffer(env, buffer_, size);
if (shBandCount) { auto builder = Material::Builder();
builder.sphericalHarmonicsBandCount(shBandCount); if (shBandCount) {
} builder.sphericalHarmonicsBandCount(shBandCount);
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality); }
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode); builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
Material* material = builder builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
.package(buffer.getData(), buffer.getSize()) Material* material = builder
.build(*engine); .package(buffer.getData(), buffer.getSize())
.build(*engine);
return (jlong) material; return (jlong) material;
});
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
@@ -25,9 +25,11 @@
#include <math/vec2.h> #include <math/vec2.h>
#include <math/vec3.h> #include <math/vec3.h>
#include <math/vec4.h> #include <math/vec4.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace filament::math; using namespace filament::math;
using namespace filament::android;
enum BooleanElement { enum BooleanElement {
BOOL, BOOL,
@@ -56,7 +58,9 @@ template<typename T>
static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) { static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0); const char *name = env->GetStringUTFChars(name_, 0);
instance->setParameter(name, v); wrapJni(env, [=]() {
instance->setParameter(name, v);
});
env->ReleaseStringUTFChars(name_, name); env->ReleaseStringUTFChars(name_, name);
} }
@@ -160,20 +164,22 @@ Java_com_google_android_filament_MaterialInstance_nSetBooleanParameterArray(JNIE
// NOTE: In C++, bool has an implementation-defined size. Here we assume // NOTE: In C++, bool has an implementation-defined size. Here we assume
// it has the same size as jboolean, which is 1 byte. // it has the same size as jboolean, which is 1 byte.
switch ((BooleanElement) element) { wrapJni(env, [=]() {
case BOOL: switch ((BooleanElement) element) {
instance->setParameter(name, ((const bool*) v) + offset, count); case BOOL:
break; instance->setParameter(name, ((const bool*) v) + offset, count);
case BOOL2: break;
instance->setParameter(name, ((const bool2*) v) + offset, count); case BOOL2:
break; instance->setParameter(name, ((const bool2*) v) + offset, count);
case BOOL3: break;
instance->setParameter(name, ((const bool3*) v) + offset, count); case BOOL3:
break; instance->setParameter(name, ((const bool3*) v) + offset, count);
case BOOL4: break;
instance->setParameter(name, ((const bool4*) v) + offset, count); case BOOL4:
break; instance->setParameter(name, ((const bool4*) v) + offset, count);
} break;
}
});
env->ReleaseBooleanArrayElements(v_, v, 0); env->ReleaseBooleanArrayElements(v_, v, 0);
@@ -190,20 +196,22 @@ Java_com_google_android_filament_MaterialInstance_nSetIntParameterArray(JNIEnv *
const char* name = env->GetStringUTFChars(name_, 0); const char* name = env->GetStringUTFChars(name_, 0);
jint* v = env->GetIntArrayElements(v_, NULL); jint* v = env->GetIntArrayElements(v_, NULL);
switch ((IntElement) element) { wrapJni(env, [=]() {
case INT: switch ((IntElement) element) {
instance->setParameter(name, ((const int32_t*) v) + offset, count); case INT:
break; instance->setParameter(name, ((const int32_t*) v) + offset, count);
case INT2: break;
instance->setParameter(name, ((const int2*) v) + offset, count); case INT2:
break; instance->setParameter(name, ((const int2*) v) + offset, count);
case INT3: break;
instance->setParameter(name, ((const int3*) v) + offset, count); case INT3:
break; instance->setParameter(name, ((const int3*) v) + offset, count);
case INT4: break;
instance->setParameter(name, ((const int4*) v) + offset, count); case INT4:
break; instance->setParameter(name, ((const int4*) v) + offset, count);
} break;
}
});
env->ReleaseIntArrayElements(v_, v, JNI_ABORT); env->ReleaseIntArrayElements(v_, v, JNI_ABORT);
@@ -220,26 +228,28 @@ Java_com_google_android_filament_MaterialInstance_nSetFloatParameterArray(JNIEnv
const char* name = env->GetStringUTFChars(name_, 0); const char* name = env->GetStringUTFChars(name_, 0);
jfloat* v = env->GetFloatArrayElements(v_, NULL); jfloat* v = env->GetFloatArrayElements(v_, NULL);
switch ((FloatElement) element) { wrapJni(env, [=]() {
case FLOAT: switch ((FloatElement) element) {
instance->setParameter(name, ((const float*) v) + offset, count); case FLOAT:
break; instance->setParameter(name, ((const float*) v) + offset, count);
case FLOAT2: break;
instance->setParameter(name, ((const float2*) v) + offset, count); case FLOAT2:
break; instance->setParameter(name, ((const float2*) v) + offset, count);
case FLOAT3: break;
instance->setParameter(name, ((const float3*) v) + offset, count); case FLOAT3:
break; instance->setParameter(name, ((const float3*) v) + offset, count);
case FLOAT4: break;
instance->setParameter(name, ((const float4*) v) + offset, count); case FLOAT4:
break; instance->setParameter(name, ((const float4*) v) + offset, count);
case MAT3: break;
instance->setParameter(name, ((const mat3f*) v) + offset, count); case MAT3:
break; instance->setParameter(name, ((const mat3f*) v) + offset, count);
case MAT4: break;
instance->setParameter(name, ((const mat4f*) v) + offset, count); case MAT4:
break; instance->setParameter(name, ((const mat4f*) v) + offset, count);
} break;
}
});
env->ReleaseFloatArrayElements(v_, v, 0); env->ReleaseFloatArrayElements(v_, v, 0);
@@ -260,7 +270,9 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
Texture* texture = (Texture*) nativeTexture; Texture* texture = (Texture*) nativeTexture;
const char *name = env->GetStringUTFChars(name_, 0); const char *name = env->GetStringUTFChars(name_, 0);
instance->setParameter(name, texture, JniUtils::from_long(sampler_)); wrapJni(env, [=]() {
instance->setParameter(name, texture, JniUtils::from_long(sampler_));
});
env->ReleaseStringUTFChars(name_, name); env->ReleaseStringUTFChars(name_, name);
} }
@@ -16,7 +16,6 @@
#include <jni.h> #include <jni.h>
#include <functional>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -24,6 +23,7 @@
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
@@ -81,11 +81,13 @@ extern "C" JNIEXPORT void JNICALL
extern "C" extern "C"
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_google_android_filament_MorphTargetBuffer_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_MorphTargetBuffer_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder *) nativeBuilder; MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@@ -97,11 +99,13 @@ Java_com_google_android_filament_MorphTargetBuffer_nSetPositionsAt(JNIEnv* env,
jint targetIndex, jfloatArray positions, jint count) { jint targetIndex, jfloatArray positions, jint count) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject; MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
jfloat* data = env->GetFloatArrayElements(positions, NULL); return filament::android::wrapJni<jint>(env, [=]() {
morphTargetBuffer->setPositionsAt(*engine, targetIndex, jfloat* data = env->GetFloatArrayElements(positions, NULL);
(math::float4*) data, size_t(count)); morphTargetBuffer->setPositionsAt(*engine, targetIndex,
env->ReleaseFloatArrayElements(positions, data, JNI_ABORT); (math::float4*) data, size_t(count));
return 0; env->ReleaseFloatArrayElements(positions, data, JNI_ABORT);
return 0;
});
} }
extern "C" extern "C"
@@ -111,11 +115,13 @@ Java_com_google_android_filament_MorphTargetBuffer_nSetTangentsAt(JNIEnv* env, j
jint targetIndex, jshortArray tangents, jint count) { jint targetIndex, jshortArray tangents, jint count) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject; MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
jshort* data = env->GetShortArrayElements(tangents, NULL); return filament::android::wrapJni<jint>(env, [=]() {
morphTargetBuffer->setTangentsAt(*engine, targetIndex, jshort* data = env->GetShortArrayElements(tangents, NULL);
(math::short4*) data, size_t(count)); morphTargetBuffer->setTangentsAt(*engine, targetIndex,
env->ReleaseShortArrayElements(tangents, data, JNI_ABORT); (math::short4*) data, size_t(count));
return 0; env->ReleaseShortArrayElements(tangents, data, JNI_ABORT);
return 0;
});
} }
extern "C" extern "C"
@@ -21,6 +21,7 @@
#include <string.h> #include <string.h>
#include <filament/RenderTarget.h> #include <filament/RenderTarget.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
@@ -72,7 +73,9 @@ Java_com_google_android_filament_RenderTarget_nBuilderBuild(JNIEnv *env, jclass
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
RenderTarget::Builder* builder = (RenderTarget::Builder*) nativeBuilder; RenderTarget::Builder* builder = (RenderTarget::Builder*) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -18,6 +18,7 @@
#include <filament/RenderableManager.h> #include <filament/RenderableManager.h>
#include <filament/MaterialInstance.h> #include <filament/MaterialInstance.h>
#include <common/JniUtils.h>
#include <utils/Entity.h> #include <utils/Entity.h>
@@ -25,6 +26,7 @@
using namespace filament; using namespace filament;
using namespace utils; using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_RenderableManager_nHasComponent(JNIEnv*, jclass, Java_com_google_android_filament_RenderableManager_nHasComponent(JNIEnv*, jclass,
@@ -63,11 +65,13 @@ Java_com_google_android_filament_RenderableManager_nDestroyBuilder(JNIEnv*, jcla
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_RenderableManager_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder, jlong nativeEngine, jint entity) { jlong nativeBuilder, jlong nativeEngine, jint entity) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return jboolean(builder->build(*engine, (Entity &) entity) == RenderableManager::Builder::Success); return wrapJni<jboolean>(env, [=]() {
return jboolean(builder->build(*engine, (Entity &) entity) == RenderableManager::Builder::Success);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -276,11 +280,13 @@ Java_com_google_android_filament_RenderableManager_nBuilderInstances(JNIEnv*, jc
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nSetSkinningBuffer(JNIEnv*, jclass, Java_com_google_android_filament_RenderableManager_nSetSkinningBuffer(JNIEnv* env, jclass,
jlong nativeRenderableManager, jint i, jlong nativeSkinningBuffer, jint count, jint offset) { jlong nativeRenderableManager, jint i, jlong nativeSkinningBuffer, jint count, jint offset) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager; RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
SkinningBuffer *sb = (SkinningBuffer *) nativeSkinningBuffer; SkinningBuffer *sb = (SkinningBuffer *) nativeSkinningBuffer;
rm->setSkinningBuffer(i, sb, count, offset); wrapJni(env, [=]() {
rm->setSkinningBuffer(i, sb, count, offset);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -295,9 +301,12 @@ Java_com_google_android_filament_RenderableManager_nSetBonesAsMatrices(JNIEnv* e
// BufferOverflowException // BufferOverflowException
return -1; return -1;
} }
rm->setBones((RenderableManager::Instance)i, jint result = 0;
static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset); wrapJni(env, [=]() {
return 0; rm->setBones((RenderableManager::Instance)i,
static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset);
});
return result;
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -312,9 +321,12 @@ Java_com_google_android_filament_RenderableManager_nSetBonesAsQuaternions(JNIEnv
// BufferOverflowException // BufferOverflowException
return -1; return -1;
} }
rm->setBones((RenderableManager::Instance)i, jint result = 0;
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset); wrapJni(env, [=]() {
return 0; rm->setBones((RenderableManager::Instance)i,
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset);
});
return result;
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -323,17 +335,21 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env,
RenderableManager *rm = (RenderableManager *) nativeRenderableManager; RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
jfloat* vec = env->GetFloatArrayElements(weights, NULL); jfloat* vec = env->GetFloatArrayElements(weights, NULL);
jsize count = env->GetArrayLength(weights); jsize count = env->GetArrayLength(weights);
rm->setMorphWeights((RenderableManager::Instance)instance, vec, count, offset); wrapJni(env, [=]() {
rm->setMorphWeights((RenderableManager::Instance)instance, vec, count, offset);
});
env->ReleaseFloatArrayElements(weights, vec, JNI_ABORT); env->ReleaseFloatArrayElements(weights, vec, JNI_ABORT);
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferOffsetAt(JNIEnv*, Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferOffsetAt(JNIEnv* env,
jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex, jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex,
jlong, jint offset) { jlong, jint offset) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager; RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
rm->setMorphTargetBufferOffsetAt((RenderableManager::Instance) i, (uint8_t) level, wrapJni(env, [=]() {
(size_t) primitiveIndex, (size_t) offset); rm->setMorphTargetBufferOffsetAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, (size_t) offset);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -344,12 +360,14 @@ Java_com_google_android_filament_RenderableManager_nGetMorphTargetCount(JNIEnv*
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nSetAxisAlignedBoundingBox(JNIEnv*, Java_com_google_android_filament_RenderableManager_nSetAxisAlignedBoundingBox(JNIEnv* env,
jclass, jlong nativeRenderableManager, jint i, jfloat cx, jfloat cy, jfloat cz, jclass, jlong nativeRenderableManager, jint i, jfloat cx, jfloat cy, jfloat cz,
jfloat ex, jfloat ey, jfloat ez) { jfloat ex, jfloat ey, jfloat ez) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager; RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
rm->setAxisAlignedBoundingBox((RenderableManager::Instance) i, {{cx, cy, cz}, wrapJni(env, [=]() {
{ex, ey, ez}}); rm->setAxisAlignedBoundingBox((RenderableManager::Instance) i, {{cx, cy, cz},
{ex, ey, ez}});
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -486,19 +504,23 @@ Java_com_google_android_filament_RenderableManager_nGetInstanceCount(JNIEnv*, jc
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nSetMaterialInstanceAt(JNIEnv*, jclass, Java_com_google_android_filament_RenderableManager_nSetMaterialInstanceAt(JNIEnv* env, jclass,
jlong nativeRenderableManager, jint i, jint primitiveIndex, jlong nativeMaterialInstance) { jlong nativeRenderableManager, jint i, jint primitiveIndex, jlong nativeMaterialInstance) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager; RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
const MaterialInstance *materialInstance = (const MaterialInstance *) nativeMaterialInstance; const MaterialInstance *materialInstance = (const MaterialInstance *) nativeMaterialInstance;
rm->setMaterialInstanceAt((RenderableManager::Instance) i, (size_t) primitiveIndex, wrapJni(env, [=]() {
materialInstance); rm->setMaterialInstanceAt((RenderableManager::Instance) i, (size_t) primitiveIndex,
materialInstance);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nClearMaterialInstanceAt(JNIEnv*, jclass, Java_com_google_android_filament_RenderableManager_nClearMaterialInstanceAt(JNIEnv* env, jclass,
jlong nativeRenderableManager, jint i, jint primitiveIndex) { jlong nativeRenderableManager, jint i, jint primitiveIndex) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager; RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
rm->clearMaterialInstanceAt((RenderableManager::Instance) i, (size_t) primitiveIndex); wrapJni(env, [=]() {
rm->clearMaterialInstanceAt((RenderableManager::Instance) i, (size_t) primitiveIndex);
});
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
@@ -22,18 +22,24 @@
#include <filament/Viewport.h> #include <filament/Viewport.h>
#include <backend/PixelBufferDescriptor.h> #include <backend/PixelBufferDescriptor.h>
#include <exception>
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include "common/JniUtils.h"
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nSkipFrame(JNIEnv *, jclass, jlong nativeRenderer, Java_com_google_android_filament_Renderer_nSkipFrame(JNIEnv *env, jclass, jlong nativeRenderer,
jlong vsyncSteadyClockTimeNano) { jlong vsyncSteadyClockTimeNano) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano)); wrapJni(env, [=]() {
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
@@ -43,37 +49,45 @@ Java_com_google_android_filament_Renderer_nShouldRenderFrame(JNIEnv *, jclass, j
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Renderer_nBeginFrame(JNIEnv *, jclass, jlong nativeRenderer, Java_com_google_android_filament_Renderer_nBeginFrame(JNIEnv *env, jclass, jlong nativeRenderer,
jlong nativeSwapChain, jlong frameTimeNanos) { jlong nativeSwapChain, jlong frameTimeNanos) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
SwapChain *swapChain = (SwapChain *) nativeSwapChain; SwapChain *swapChain = (SwapChain *) nativeSwapChain;
return (jboolean) renderer->beginFrame(swapChain, uint64_t(frameTimeNanos)); return wrapJniBackend<jboolean>(env, [=]() {
return (jboolean) renderer->beginFrame(swapChain, uint64_t(frameTimeNanos));
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nEndFrame(JNIEnv *, jclass, jlong nativeRenderer) { Java_com_google_android_filament_Renderer_nEndFrame(JNIEnv *env, jclass, jlong nativeRenderer) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
renderer->endFrame(); wrapJniBackend(env, [=]() {
renderer->endFrame();
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nRender(JNIEnv *, jclass, jlong nativeRenderer, Java_com_google_android_filament_Renderer_nRender(JNIEnv *env, jclass, jlong nativeRenderer,
jlong nativeView) { jlong nativeView) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
View *view = (View *) nativeView; View *view = (View *) nativeView;
renderer->render(view); wrapJniBackend(env, [=]() {
renderer->render(view);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nRenderStandaloneView(JNIEnv *, jclass, jlong nativeRenderer, Java_com_google_android_filament_Renderer_nRenderStandaloneView(JNIEnv *env, jclass, jlong nativeRenderer,
jlong nativeView) { jlong nativeView) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
View *view = (View *) nativeView; View *view = (View *) nativeView;
renderer->renderStandaloneView(view); wrapJni(env, [=]() {
renderer->renderStandaloneView(view);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nCopyFrame(JNIEnv *, jclass, jlong nativeRenderer, Java_com_google_android_filament_Renderer_nCopyFrame(JNIEnv *env, jclass, jlong nativeRenderer,
jlong nativeDstSwapChain, jlong nativeDstSwapChain,
jint dstLeft, jint dstBottom, jint dstWidth, jint dstHeight, jint dstLeft, jint dstBottom, jint dstWidth, jint dstHeight,
jint srcLeft, jint srcBottom, jint srcWidth, jint srcHeight, jint srcLeft, jint srcBottom, jint srcWidth, jint srcHeight,
@@ -82,7 +96,9 @@ Java_com_google_android_filament_Renderer_nCopyFrame(JNIEnv *, jclass, jlong nat
SwapChain *dstSwapChain = (SwapChain *) nativeDstSwapChain; SwapChain *dstSwapChain = (SwapChain *) nativeDstSwapChain;
const filament::Viewport dstViewport {dstLeft, dstBottom, (uint32_t) dstWidth, (uint32_t) dstHeight}; const filament::Viewport dstViewport {dstLeft, dstBottom, (uint32_t) dstWidth, (uint32_t) dstHeight};
const filament::Viewport srcViewport {srcLeft, srcBottom, (uint32_t) srcWidth, (uint32_t) srcHeight}; const filament::Viewport srcViewport {srcLeft, srcBottom, (uint32_t) srcWidth, (uint32_t) srcHeight};
renderer->copyFrame(dstSwapChain, dstViewport, srcViewport, (uint32_t) flags); wrapJni(env, [=]() {
renderer->copyFrame(dstSwapChain, dstViewport, srcViewport, (uint32_t) flags);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -114,10 +130,11 @@ Java_com_google_android_filament_Renderer_nReadPixels(JNIEnv *env, jclass,
(uint32_t) stride, (uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
renderer->readPixels(uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height), return wrapJni<jint>(env, [&]() {
std::move(desc)); renderer->readPixels(uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0; return 0;
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -150,23 +167,28 @@ Java_com_google_android_filament_Renderer_nReadPixelsEx(JNIEnv *env, jclass,
(uint32_t) stride, (uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
renderer->readPixels(renderTarget, return wrapJni<jint>(env, [&]() {
uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height), renderer->readPixels(renderTarget,
std::move(desc)); uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0; return 0;
});
} }
extern "C" JNIEXPORT jdouble JNICALL extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_android_filament_Renderer_nGetUserTime(JNIEnv*, jclass, jlong nativeRenderer) { Java_com_google_android_filament_Renderer_nGetUserTime(JNIEnv *env, jclass, jlong nativeRenderer) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
return renderer->getUserTime(); return wrapJni<jdouble>(env, [=]() {
return renderer->getUserTime();
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nResetUserTime(JNIEnv*, jclass, jlong nativeRenderer) { Java_com_google_android_filament_Renderer_nResetUserTime(JNIEnv *env, jclass, jlong nativeRenderer) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
renderer->resetUserTime(); wrapJni(env, [=]() {
renderer->resetUserTime();
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -186,20 +208,24 @@ Java_com_google_android_filament_Renderer_nSetFrameRateOptions(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nSetClearOptions(JNIEnv *, jclass , Java_com_google_android_filament_Renderer_nSetClearOptions(JNIEnv *env, jclass ,
jlong nativeRenderer, jfloat r, jfloat g, jfloat b, jfloat a, jlong nativeRenderer, jfloat r, jfloat g, jfloat b, jfloat a,
jboolean clear, jboolean discard) { jboolean clear, jboolean discard) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
renderer->setClearOptions({ .clearColor = {r, g, b, a}, wrapJni(env, [=]() {
.clear = (bool) clear, renderer->setClearOptions({ .clearColor = {r, g, b, a},
.discard = (bool) discard}); .clear = (bool) clear,
.discard = (bool) discard});
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nSetPresentationTime(JNIEnv *, jclass , Java_com_google_android_filament_Renderer_nSetPresentationTime(JNIEnv *env, jclass ,
jlong nativeRenderer, jlong monotonicClockNanos) { jlong nativeRenderer, jlong monotonicClockNanos) {
Renderer *renderer = (Renderer *) nativeRenderer; Renderer *renderer = (Renderer *) nativeRenderer;
renderer->setPresentationTime(monotonicClockNanos); wrapJni(env, [=]() {
renderer->setPresentationTime(monotonicClockNanos);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -19,9 +19,11 @@
#include <filament/Scene.h> #include <filament/Scene.h>
#include <utils/Entity.h> #include <utils/Entity.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace utils; using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nSetSkybox(JNIEnv *env, jclass type, jlong nativeScene, Java_com_google_android_filament_Scene_nSetSkybox(JNIEnv *env, jclass type, jlong nativeScene,
@@ -43,15 +45,20 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nAddEntity(JNIEnv *env, jclass type, jlong nativeScene, Java_com_google_android_filament_Scene_nAddEntity(JNIEnv *env, jclass type, jlong nativeScene,
jint entity) { jint entity) {
Scene* scene = (Scene*) nativeScene; Scene* scene = (Scene*) nativeScene;
scene->addEntity((Entity&) entity); wrapJni(env, [=]() {
scene->addEntity((Entity&) entity);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nAddEntities(JNIEnv *env, jclass type, jlong nativeScene, Java_com_google_android_filament_Scene_nAddEntities(JNIEnv *env, jclass type, jlong nativeScene,
jintArray entities) { jintArray entities) {
Scene* scene = (Scene*) nativeScene; Scene* scene = (Scene*) nativeScene;
jsize length = env->GetArrayLength(entities);
Entity* nativeEntities = (Entity*) env->GetIntArrayElements(entities, nullptr); Entity* nativeEntities = (Entity*) env->GetIntArrayElements(entities, nullptr);
scene->addEntities(nativeEntities, env->GetArrayLength(entities)); wrapJni(env, [=]() {
scene->addEntities(nativeEntities, length);
});
env->ReleaseIntArrayElements(entities, (jint*) nativeEntities, JNI_ABORT); env->ReleaseIntArrayElements(entities, (jint*) nativeEntities, JNI_ABORT);
} }
@@ -59,15 +66,20 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nRemove(JNIEnv *env, jclass type, jlong nativeScene, Java_com_google_android_filament_Scene_nRemove(JNIEnv *env, jclass type, jlong nativeScene,
jint entity) { jint entity) {
Scene* scene = (Scene*) nativeScene; Scene* scene = (Scene*) nativeScene;
scene->remove((Entity&) entity); wrapJni(env, [=]() {
scene->remove((Entity&) entity);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nRemoveEntities(JNIEnv *env, jclass type, jlong nativeScene, Java_com_google_android_filament_Scene_nRemoveEntities(JNIEnv *env, jclass type, jlong nativeScene,
jintArray entities) { jintArray entities) {
Scene* scene = (Scene*) nativeScene; Scene* scene = (Scene*) nativeScene;
jsize length = env->GetArrayLength(entities);
Entity* nativeEntities = (Entity*) env->GetIntArrayElements(entities, nullptr); Entity* nativeEntities = (Entity*) env->GetIntArrayElements(entities, nullptr);
scene->removeEntities(nativeEntities, env->GetArrayLength(entities)); wrapJni(env, [=]() {
scene->removeEntities(nativeEntities, length);
});
env->ReleaseIntArrayElements(entities, (jint*) nativeEntities, JNI_ABORT); env->ReleaseIntArrayElements(entities, (jint*) nativeEntities, JNI_ABORT);
} }
@@ -16,7 +16,6 @@
#include <jni.h> #include <jni.h>
#include <functional>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -24,6 +23,7 @@
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
@@ -60,11 +60,13 @@ Java_com_google_android_filament_SkinningBuffer_nBuilderInitialize(JNIEnv*, jcla
extern "C" extern "C"
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_google_android_filament_SkinningBuffer_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_SkinningBuffer_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
SkinningBuffer::Builder* builder = (SkinningBuffer::Builder *) nativeBuilder; SkinningBuffer::Builder* builder = (SkinningBuffer::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@@ -76,16 +78,18 @@ Java_com_google_android_filament_SkinningBuffer_nSetBonesAsMatrices(JNIEnv* env,
jint offset) { jint offset) {
SkinningBuffer *skinningBuffer = (SkinningBuffer *) nativeSkinningBuffer; SkinningBuffer *skinningBuffer = (SkinningBuffer *) nativeSkinningBuffer;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, matrices, boneCount * 16); return filament::android::wrapJni<jint>(env, [=]() {
void* data = nioBuffer.getData(); AutoBuffer nioBuffer(env, matrices, boneCount * 16);
size_t sizeInBytes = nioBuffer.getSize(); void* data = nioBuffer.getData();
if (sizeInBytes > (remaining << nioBuffer.getShift())) { size_t sizeInBytes = nioBuffer.getSize();
// BufferOverflowException if (sizeInBytes > (remaining << nioBuffer.getShift())) {
return -1; // BufferOverflowException
} return -1;
skinningBuffer->setBones(*engine, }
static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset); skinningBuffer->setBones(*engine,
return 0; static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
});
} }
extern "C" extern "C"
@@ -95,16 +99,18 @@ Java_com_google_android_filament_SkinningBuffer_nSetBonesAsQuaternions(JNIEnv* e
jint boneCount, jint offset) { jint boneCount, jint offset) {
SkinningBuffer *skinningBuffer = (SkinningBuffer *) nativeSkinningBuffer; SkinningBuffer *skinningBuffer = (SkinningBuffer *) nativeSkinningBuffer;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, quaternions, boneCount * 8); return filament::android::wrapJni<jint>(env, [=]() {
void* data = nioBuffer.getData(); AutoBuffer nioBuffer(env, quaternions, boneCount * 8);
size_t sizeInBytes = nioBuffer.getSize(); void* data = nioBuffer.getData();
if (sizeInBytes > (remaining << nioBuffer.getShift())) { size_t sizeInBytes = nioBuffer.getSize();
// BufferOverflowException if (sizeInBytes > (remaining << nioBuffer.getShift())) {
return -1; // BufferOverflowException
} return -1;
skinningBuffer->setBones(*engine, }
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset); skinningBuffer->setBones(*engine,
return 0; static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
});
} }
extern "C" extern "C"
@@ -19,6 +19,7 @@
#include <filament/Skybox.h> #include <filament/Skybox.h>
#include <math/vec4.h> #include <math/vec4.h>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
@@ -76,7 +77,9 @@ Java_com_google_android_filament_Skybox_nBuilderBuild(JNIEnv *env, jclass type,
jlong nativeSkyBoxBuilder, jlong nativeEngine) { jlong nativeSkyBoxBuilder, jlong nativeEngine) {
Skybox::Builder *builder = (Skybox::Builder *) nativeSkyBoxBuilder; Skybox::Builder *builder = (Skybox::Builder *) nativeSkyBoxBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -23,6 +23,7 @@
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include <common/JniUtils.h>
#ifdef __ANDROID__ #ifdef __ANDROID__
@@ -113,11 +114,13 @@ Java_com_google_android_filament_Stream_nBuilderHeight(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Stream_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_Stream_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeStreamBuilder, jlong nativeEngine) { jlong nativeStreamBuilder, jlong nativeEngine) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder; StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
return (jlong) builder->builder()->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->builder()->build(*engine);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -21,6 +21,7 @@
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include <algorithm> #include <algorithm>
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace filament::geometry; using namespace filament::geometry;
@@ -125,7 +126,9 @@ extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_SurfaceOrientation_nBuilderBuild(JNIEnv* env, jclass, Java_com_google_android_filament_SurfaceOrientation_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder) { jlong nativeBuilder) {
auto wrapper = (JniWrapper *) nativeBuilder; auto wrapper = (JniWrapper *) nativeBuilder;
return (jlong) wrapper->builder->build(); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) wrapper->builder->build();
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -20,6 +20,7 @@
#include <filament/SwapChain.h> #include <filament/SwapChain.h>
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
@@ -58,11 +59,13 @@ Java_com_google_android_filament_SwapChain_nSetFrameScheduledCallback(JNIEnv* en
jlong nativeSwapChain, jobject handler, jobject runnable) { jlong nativeSwapChain, jobject handler, jobject runnable) {
SwapChain* swapChain = (SwapChain*) nativeSwapChain; SwapChain* swapChain = (SwapChain*) nativeSwapChain;
auto* callback = JniCallback::make(env, handler, runnable); auto* callback = JniCallback::make(env, handler, runnable);
swapChain->setFrameScheduledCallback(callback->getHandler(), filament::android::wrapJni<void>(env, [=]() {
[callback](backend::PresentCallable) { swapChain->setFrameScheduledCallback(callback->getHandler(),
// Ignore PresentCallable, which is only meaningful with the Metal backend. [callback](backend::PresentCallable) {
JniCallback::postToJavaAndDestroy(callback); // Ignore PresentCallable, which is only meaningful with the Metal backend.
}); JniCallback::postToJavaAndDestroy(callback);
});
});
} }
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
@@ -17,7 +17,6 @@
#include <jni.h> #include <jni.h>
#include <algorithm> #include <algorithm>
#include <functional>
#ifdef __ANDROID__ #ifdef __ANDROID__
#include <android/bitmap.h> #include <android/bitmap.h>
@@ -38,12 +37,14 @@
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include <common/JniUtils.h>
#include "private/backend/VirtualMachineEnv.h" #include "private/backend/VirtualMachineEnv.h"
using namespace filament; using namespace filament;
using namespace backend; using namespace backend;
using namespace filament::android;
static size_t getTextureDataSize(const Texture *texture, static size_t getTextureDataSize(const Texture *texture,
size_t level, Texture::Format format, Texture::Type type, size_t level, Texture::Format format, Texture::Type type,
@@ -273,12 +274,13 @@ Java_com_google_android_filament_Texture_nSetImage3D(JNIEnv* env, jclass, jlong
(uint32_t) stride, (uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
texture->setImage(*engine, (size_t) level, return wrapJni<jint>(env, [&]() {
(uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset, texture->setImage(*engine, (size_t) level,
(uint32_t) width, (uint32_t) height, (uint32_t) depth, (uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
std::move(desc)); (uint32_t) width, (uint32_t) height, (uint32_t) depth,
std::move(desc));
return 0; return 0;
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -307,12 +309,13 @@ Java_com_google_android_filament_Texture_nSetImage3DCompressed(JNIEnv *env, jcla
(backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes, (backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
texture->setImage(*engine, (size_t) level, return wrapJni<jint>(env, [&]() {
(uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset, texture->setImage(*engine, (size_t) level,
(uint32_t) width, (uint32_t) height, (uint32_t) depth, (uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
std::move(desc)); (uint32_t) width, (uint32_t) height, (uint32_t) depth,
std::move(desc));
return 0; return 0;
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -346,12 +349,13 @@ Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass,
(uint32_t) stride, (uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
return wrapJni<jint>(env, [&]() {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdeprecated-declarations"
texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets); texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets);
#pragma clang diagnostic pop #pragma clang diagnostic pop
return 0;
return 0; });
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -384,20 +388,23 @@ Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env,
(backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes, (backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
return wrapJni<jint>(env, [&]() {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdeprecated-declarations"
texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets); texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets);
#pragma clang diagnostic pop #pragma clang diagnostic pop
return 0;
return 0; });
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv*, jclass, jlong nativeTexture, Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv* env, jclass, jlong nativeTexture,
jlong nativeEngine, jlong eglImage) { jlong nativeEngine, jlong eglImage) {
Texture *texture = (Texture *) nativeTexture; Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
texture->setExternalImage(*engine, (void*)eglImage); wrapJni(env, [=]() {
texture->setExternalImage(*engine, (void*)eglImage);
});
} }
extern "C" extern "C"
@@ -418,33 +425,35 @@ Java_com_google_android_filament_Texture_nSetExternalImageByAHB(JNIEnv *env, jcl
return JNI_FALSE; return JNI_FALSE;
} }
if (engine->getBackend() == Backend::OPENGL) { return wrapJni<jboolean>(env, [=]() {
// CAVEAT: we assume that Backend::OPENGL on Android implies PlatformEGLAndroid. if (engine->getBackend() == Backend::OPENGL) {
// CAVEAT: we assume that Backend::OPENGL on Android implies PlatformEGLAndroid.
#if UTILS_HAS_RTTI #if UTILS_HAS_RTTI
if (!dynamic_cast<PlatformEGLAndroid*>(platform)) { if (!dynamic_cast<PlatformEGLAndroid*>(platform)) {
return JNI_FALSE; return JNI_FALSE;
} }
#endif #endif
auto* eglPlatform = (PlatformEGLAndroid*) platform; auto* eglPlatform = (PlatformEGLAndroid*) platform;
auto ref = eglPlatform->createExternalImage(nativeBuffer, false); auto ref = eglPlatform->createExternalImage(nativeBuffer, false);
texture->setExternalImage(*engine, ref); texture->setExternalImage(*engine, ref);
} }
#if FILAMENT_SUPPORTS_VULKAN #if FILAMENT_SUPPORTS_VULKAN
else if (engine->getBackend() == Backend::VULKAN) { else if (engine->getBackend() == Backend::VULKAN) {
// CAVEAT: we assume that Backend::VULKAN on Android implies VulkanPlatformAndroid. // CAVEAT: we assume that Backend::VULKAN on Android implies VulkanPlatformAndroid.
#if UTILS_HAS_RTTI #if UTILS_HAS_RTTI
if (!dynamic_cast<VulkanPlatformAndroid*>(platform)) { if (!dynamic_cast<VulkanPlatformAndroid*>(platform)) {
return JNI_FALSE; return JNI_FALSE;
} }
#endif #endif
auto* vulkanPlatform = (VulkanPlatformAndroid*) platform; auto* vulkanPlatform = (VulkanPlatformAndroid*) platform;
auto ref = vulkanPlatform->createExternalImage(nativeBuffer, false); auto ref = vulkanPlatform->createExternalImage(nativeBuffer, false);
texture->setExternalImage(*engine, ref); texture->setExternalImage(*engine, ref);
} }
#endif // FILAMENT_SUPPORTS_VULKAN #endif // FILAMENT_SUPPORTS_VULKAN
// success! // success!
return JNI_TRUE; return JNI_TRUE;
});
#else #else
// other platforms could come here // other platforms could come here
return JNI_FALSE; return JNI_FALSE;
@@ -452,20 +461,24 @@ Java_com_google_android_filament_Texture_nSetExternalImageByAHB(JNIEnv *env, jcl
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv*, jclass, Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv* env, jclass,
jlong nativeTexture, jlong nativeEngine, jlong nativeStream) { jlong nativeTexture, jlong nativeEngine, jlong nativeStream) {
Texture *texture = (Texture *) nativeTexture; Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
Stream *stream = (Stream *) nativeStream; Stream *stream = (Stream *) nativeStream;
texture->setExternalStream(*engine, stream); wrapJni(env, [=]() {
texture->setExternalStream(*engine, stream);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nGenerateMipmaps(JNIEnv*, jclass, Java_com_google_android_filament_Texture_nGenerateMipmaps(JNIEnv* env, jclass,
jlong nativeTexture, jlong nativeEngine) { jlong nativeTexture, jlong nativeEngine) {
Texture *texture = (Texture *) nativeTexture; Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
texture->generateMipmaps(*engine); wrapJni(env, [=]() {
texture->generateMipmaps(*engine);
});
} }
extern "C" extern "C"
@@ -515,9 +528,10 @@ Java_com_google_android_filament_Texture_nGeneratePrefilterMipmap(JNIEnv *env, j
options.sampleCount = sampleCount; options.sampleCount = sampleCount;
options.mirror = mirror; options.mirror = mirror;
filament::generatePrefilterMipmap(texture, *engine, std::move(desc), faceOffsets, &options); return wrapJni<jint>(env, [&]() {
filament::generatePrefilterMipmap(texture, *engine, std::move(desc), faceOffsets, &options);
return 0; return 0;
});
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -640,10 +654,12 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmap(JNIEnv* env, j
autoBitmap->getType(format), autoBitmap->getType(format),
&AutoBitmap::invokeNoCallback, autoBitmap); &AutoBitmap::invokeNoCallback, autoBitmap);
texture->setImage(*engine, (size_t) level, filament::android::wrapJni(env, [&]() {
(uint32_t) xoffset, (uint32_t) yoffset, texture->setImage(*engine, (size_t) level,
(uint32_t) width, (uint32_t) height, (uint32_t) xoffset, (uint32_t) yoffset,
std::move(desc)); (uint32_t) width, (uint32_t) height,
std::move(desc));
});
} }
extern "C" extern "C"
@@ -663,10 +679,12 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmapWithCallback(JN
autoBitmap->getType(format), autoBitmap->getType(format),
autoBitmap->getHandler(), &AutoBitmap::invoke, autoBitmap); autoBitmap->getHandler(), &AutoBitmap::invoke, autoBitmap);
texture->setImage(*engine, (size_t) level, filament::android::wrapJni(env, [&]() {
(uint32_t) xoffset, (uint32_t) yoffset, texture->setImage(*engine, (size_t) level,
(uint32_t) width, (uint32_t) height, (uint32_t) xoffset, (uint32_t) yoffset,
std::move(desc)); (uint32_t) width, (uint32_t) height,
std::move(desc));
});
} }
#endif #endif
@@ -22,9 +22,11 @@
#include <utils/Entity.h> #include <utils/Entity.h>
#include <math/mat4.h> #include <math/mat4.h>
#include <common/JniUtils.h>
using namespace utils; using namespace utils;
using namespace filament; using namespace filament;
using namespace filament::android;
static_assert(sizeof(jint) == sizeof(Entity), "jint and Entity are not compatible!!"); static_assert(sizeof(jint) == sizeof(Entity), "jint and Entity are not compatible!!");
@@ -45,12 +47,14 @@ Java_com_google_android_filament_TransformManager_nGetInstance(JNIEnv*, jclass,
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_TransformManager_nCreate(JNIEnv*, jclass, Java_com_google_android_filament_TransformManager_nCreate(JNIEnv* env, jclass,
jlong nativeTransformManager, jint entity_) { jlong nativeTransformManager, jint entity_) {
TransformManager* tm = (TransformManager*) nativeTransformManager; TransformManager* tm = (TransformManager*) nativeTransformManager;
Entity& entity = *reinterpret_cast<Entity*>(&entity_); return wrapJni<jint>(env, [=]() {
tm->create(entity); Entity entity = *reinterpret_cast<const Entity*>(&entity_);
return tm->getInstance(entity); tm->create(entity);
return tm->getInstance(entity);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -58,16 +62,18 @@ Java_com_google_android_filament_TransformManager_nCreateArray(JNIEnv* env,
jclass, jlong nativeTransformManager, jint entity_, jint parent, jclass, jlong nativeTransformManager, jint entity_, jint parent,
jfloatArray localTransform_) { jfloatArray localTransform_) {
TransformManager* tm = (TransformManager*) nativeTransformManager; TransformManager* tm = (TransformManager*) nativeTransformManager;
Entity& entity = *reinterpret_cast<Entity*>(&entity_); return wrapJni<jint>(env, [=]() {
if (localTransform_) { Entity entity = *reinterpret_cast<const Entity*>(&entity_);
jfloat *localTransform = env->GetFloatArrayElements(localTransform_, NULL); if (localTransform_) {
tm->create(entity, (TransformManager::Instance) parent, jfloat *localTransform = env->GetFloatArrayElements(localTransform_, NULL);
*reinterpret_cast<const filament::math::mat4f *>(localTransform)); tm->create(entity, (TransformManager::Instance) parent,
env->ReleaseFloatArrayElements(localTransform_, localTransform, JNI_ABORT); *reinterpret_cast<const filament::math::mat4f *>(localTransform));
} else { env->ReleaseFloatArrayElements(localTransform_, localTransform, JNI_ABORT);
tm->create(entity, (TransformManager::Instance) parent); } else {
} tm->create(entity, (TransformManager::Instance) parent);
return tm->getInstance(entity); }
return tm->getInstance(entity);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -75,16 +81,18 @@ Java_com_google_android_filament_TransformManager_nCreateArrayFp64(JNIEnv* env,
jclass, jlong nativeTransformManager, jint entity_, jint parent, jclass, jlong nativeTransformManager, jint entity_, jint parent,
jdoubleArray localTransform_) { jdoubleArray localTransform_) {
TransformManager* tm = (TransformManager*) nativeTransformManager; TransformManager* tm = (TransformManager*) nativeTransformManager;
Entity& entity = *reinterpret_cast<Entity*>(&entity_); return wrapJni<jint>(env, [=]() {
if (localTransform_) { Entity entity = *reinterpret_cast<const Entity*>(&entity_);
jdouble *localTransform = env->GetDoubleArrayElements(localTransform_, NULL); if (localTransform_) {
tm->create(entity, (TransformManager::Instance) parent, jdouble *localTransform = env->GetDoubleArrayElements(localTransform_, NULL);
*reinterpret_cast<const filament::math::mat4 *>(localTransform)); tm->create(entity, (TransformManager::Instance) parent,
env->ReleaseDoubleArrayElements(localTransform_, localTransform, JNI_ABORT); *reinterpret_cast<const filament::math::mat4 *>(localTransform));
} else { env->ReleaseDoubleArrayElements(localTransform_, localTransform, JNI_ABORT);
tm->create(entity, (TransformManager::Instance) parent); } else {
} tm->create(entity, (TransformManager::Instance) parent);
return tm->getInstance(entity); }
return tm->getInstance(entity);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -26,6 +26,7 @@
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include "common/NioUtils.h" #include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament; using namespace filament;
using namespace filament::math; using namespace filament::math;
@@ -82,11 +83,13 @@ Java_com_google_android_filament_VertexBuffer_nBuilderNormalized(JNIEnv*, jclass
} }
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_VertexBuffer_nBuilderBuild(JNIEnv*, jclass, Java_com_google_android_filament_VertexBuffer_nBuilderBuild(JNIEnv* env, jclass,
jlong nativeBuilder, jlong nativeEngine) { jlong nativeBuilder, jlong nativeEngine) {
VertexBuffer::Builder* builder = (VertexBuffer::Builder *) nativeBuilder; VertexBuffer::Builder* builder = (VertexBuffer::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine); return filament::android::wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
} }
extern "C" JNIEXPORT jint JNICALL extern "C" JNIEXPORT jint JNICALL
@@ -104,30 +107,34 @@ Java_com_google_android_filament_VertexBuffer_nSetBufferAt(JNIEnv *env, jclass,
VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer; VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, buffer, count); return filament::android::wrapJni<jint>(env, [=]() {
void* data = nioBuffer.getData(); AutoBuffer nioBuffer(env, buffer, count);
size_t sizeInBytes = nioBuffer.getSize(); void* data = nioBuffer.getData();
if (sizeInBytes > (remaining << nioBuffer.getShift())) { size_t sizeInBytes = nioBuffer.getSize();
// BufferOverflowException if (sizeInBytes > (remaining << nioBuffer.getShift())) {
return -1; // BufferOverflowException
} return -1;
}
auto* callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer)); auto* callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
BufferDescriptor desc(data, sizeInBytes, BufferDescriptor desc(data, sizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback); callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
vertexBuffer->setBufferAt(*engine, (uint8_t) bufferIndex, std::move(desc), vertexBuffer->setBufferAt(*engine, (uint8_t) bufferIndex, std::move(desc),
(uint32_t) destOffsetInBytes); (uint32_t) destOffsetInBytes);
return 0; return 0;
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_VertexBuffer_nSetBufferObjectAt(JNIEnv*, jclass, Java_com_google_android_filament_VertexBuffer_nSetBufferObjectAt(JNIEnv* env, jclass,
jlong nativeVertexBuffer, jlong nativeEngine, jint bufferIndex, jlong nativeBufferObject) { jlong nativeVertexBuffer, jlong nativeEngine, jint bufferIndex, jlong nativeBufferObject) {
VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer; VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer;
Engine *engine = (Engine *) nativeEngine; Engine *engine = (Engine *) nativeEngine;
BufferObject *bufferObject = (BufferObject *) nativeBufferObject; BufferObject *bufferObject = (BufferObject *) nativeBufferObject;
vertexBuffer->setBufferObjectAt(*engine, (uint8_t) bufferIndex, bufferObject); filament::android::wrapJni(env, [=]() {
vertexBuffer->setBufferObjectAt(*engine, (uint8_t) bufferIndex, bufferObject);
});
} }
+18 -8
View File
@@ -21,10 +21,12 @@
#include <filament/Viewport.h> #include <filament/Viewport.h>
#include "common/CallbackUtils.h" #include "common/CallbackUtils.h"
#include <common/JniUtils.h>
#include "private/backend/VirtualMachineEnv.h" #include "private/backend/VirtualMachineEnv.h"
using namespace filament; using namespace filament;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetName(JNIEnv* env, jclass, jlong nativeView, jstring name_) { Java_com_google_android_filament_View_nSetName(JNIEnv* env, jclass, jlong nativeView, jstring name_) {
@@ -35,10 +37,12 @@ Java_com_google_android_filament_View_nSetName(JNIEnv* env, jclass, jlong native
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetScene(JNIEnv*, jclass, jlong nativeView, jlong nativeScene) { Java_com_google_android_filament_View_nSetScene(JNIEnv* env, jclass, jlong nativeView, jlong nativeScene) {
View* view = (View*) nativeView; View* view = (View*) nativeView;
Scene* scene = (Scene*) nativeScene; Scene* scene = (Scene*) nativeScene;
view->setScene(scene); wrapJni(env, [=]() {
view->setScene(scene);
});
} }
extern "C" JNIEXPORT void JNICALL extern "C" JNIEXPORT void JNICALL
@@ -549,21 +553,27 @@ Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass,
extern "C" extern "C"
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetMaterialGlobal(JNIEnv * , jclass, jlong nativeView, Java_com_google_android_filament_View_nSetMaterialGlobal(JNIEnv *env, jclass, jlong nativeView,
jint index, jfloat x, jfloat y, jfloat z, jfloat w) { jint index, jfloat x, jfloat y, jfloat z, jfloat w) {
View *view = (View *) nativeView; View *view = (View *) nativeView;
view->setMaterialGlobal((uint32_t)index, { x, y, z, w }); wrapJni(env, [=]() {
view->setMaterialGlobal((uint32_t)index, { x, y, z, w });
});
} }
extern "C" extern "C"
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nGetMaterialGlobal(JNIEnv *env, jclass clazz, Java_com_google_android_filament_View_nGetMaterialGlobal(JNIEnv *env, jclass clazz,
jlong nativeView, jint index, jfloatArray out_) { jlong nativeView, jint index, jfloatArray out_) {
jfloat* out = env->GetFloatArrayElements(out_, nullptr);
View *view = (View *) nativeView; View *view = (View *) nativeView;
auto result = view->getMaterialGlobal(index); wrapJni(env, [=]() {
std::copy_n(result.v, 4, out); auto result = view->getMaterialGlobal(index);
env->ReleaseFloatArrayElements(out_, out, 0); jfloat* out = env->GetFloatArrayElements(out_, nullptr);
if (out) {
std::copy_n(result.v, 4, out);
env->ReleaseFloatArrayElements(out_, out, 0);
}
});
} }
extern "C" extern "C"
@@ -91,6 +91,8 @@ public class BufferObject {
* @return the newly created <code>BufferObject</code> object * @return the newly created <code>BufferObject</code> object
* *
* @exception IllegalStateException if the BufferObject could not be created * @exception IllegalStateException if the BufferObject could not be created
* @throws RuntimeException if a runtime error occurred, such as running out of
* memory or other resources.
* *
* @see #setBuffer * @see #setBuffer
*/ */
@@ -321,8 +321,7 @@ public class Engine {
* be initialized, for instance if it doesn't support the right version of OpenGL or * be initialized, for instance if it doesn't support the right version of OpenGL or
* OpenGL ES. * OpenGL ES.
* *
* @exception IllegalStateException can be thrown if there isn't enough memory to * @throws Error if there isn't enough memory to allocate the command buffer.
* allocate the command buffer.
*/ */
public Engine build() { public Engine build() {
long nativeEngine = nBuilderBuild(mNativeBuilder); long nativeEngine = nBuilderBuild(mNativeBuilder);
@@ -729,6 +728,8 @@ public class Engine {
* *
* @return the active feature level. * @return the active feature level.
* *
* @throws RuntimeException if the feature level cannot be set.
*
* @see Builder#featureLevel * @see Builder#featureLevel
* @see #getSupportedFeatureLevel * @see #getSupportedFeatureLevel
* @see #getActiveFeatureLevel * @see #getActiveFeatureLevel
@@ -775,6 +776,15 @@ public class Engine {
return nIsAutomaticInstancingEnabled(getNativeObject()); return nIsAutomaticInstancingEnabled(getNativeObject());
} }
/**
* Returns whether the engine has encountered an unrecoverable failure.
*
* @return true if an unrecoverable failure has occurred, false otherwise.
*/
public boolean hasUnrecoverableFailure() {
return nHasUnrecoverableFailure(getNativeObject());
}
/** /**
* Retrieves the configuration settings of this {@link Engine}. * Retrieves the configuration settings of this {@link Engine}.
* *
@@ -1261,9 +1271,10 @@ public class Engine {
* Destroys a {@link Material} and frees all its associated resources. * Destroys a {@link Material} and frees all its associated resources.
* <p> * <p>
* All {@link MaterialInstance} of the specified {@link Material} must be destroyed before * All {@link MaterialInstance} of the specified {@link Material} must be destroyed before
* destroying it; if some {@link MaterialInstance} remain, this method fails silently. * destroying it.
* *
* @param material the {@link Material} to destroy * @param material the {@link Material} to destroy
* @throws RuntimeException if some MaterialInstances remain.
*/ */
public void destroyMaterial(@NonNull Material material) { public void destroyMaterial(@NonNull Material material) {
assertDestroy(nDestroyMaterial(getNativeObject(), material.getNativeObject())); assertDestroy(nDestroyMaterial(getNativeObject(), material.getNativeObject()));
@@ -1580,6 +1591,7 @@ public class Engine {
private static native long nGetEntityManager(long nativeEngine); private static native long nGetEntityManager(long nativeEngine);
private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable); private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable);
private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine); private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine);
private static native boolean nHasUnrecoverableFailure(long nativeEngine);
private static native long nGetMaxStereoscopicEyes(long nativeEngine); private static native long nGetMaxStereoscopicEyes(long nativeEngine);
private static native int nGetSupportedFeatureLevel(long nativeEngine); private static native int nGetSupportedFeatureLevel(long nativeEngine);
private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal); private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal);
@@ -38,7 +38,17 @@ public class Fence {
} }
/** /**
* Client-side wait on the Fence.
*
* Blocks the current thread until the Fence signals. * Blocks the current thread until the Fence signals.
*
* @param mode Whether the command stream is flushed before waiting or not.
* @param timeoutNanoSeconds Wait time out in nanoseconds. Using a timeout of 0 is a way to query the state of the fence.
* A timeout value of WAIT_FOR_EVER is used to disable the timeout.
* @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::TIMEOUT_EXPIRED if the time out expired or
* FenceStatus::ERROR in other cases.
* @throws Error if the backend thread encountered an unrecoverable error.
*/ */
public FenceStatus wait(@NonNull Mode mode, long timeoutNanoSeconds) { public FenceStatus wait(@NonNull Mode mode, long timeoutNanoSeconds) {
int nativeResult = nWait(getNativeObject(), mode.ordinal(), timeoutNanoSeconds); int nativeResult = nWait(getNativeObject(), mode.ordinal(), timeoutNanoSeconds);
@@ -55,6 +65,15 @@ public class Fence {
} }
} }
/**
* Client-side wait on a Fence and destroy the Fence.
*
* @param fence Fence object to wait on.
* @param mode Whether the command stream is flushed before waiting or not.
* @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::ERROR otherwise.
* @throws Error if the backend thread encountered an unrecoverable error.
*/
public static FenceStatus waitAndDestroy(@NonNull Fence fence, @NonNull Mode mode) { public static FenceStatus waitAndDestroy(@NonNull Fence fence, @NonNull Mode mode) {
int nativeResult = nWaitAndDestroy(fence.getNativeObject(), mode.ordinal()); int nativeResult = nWaitAndDestroy(fence.getNativeObject(), mode.ordinal());
switch (nativeResult) { switch (nativeResult) {
@@ -99,6 +99,7 @@ public class IndexBuffer {
* @return the newly created <code>IndexBuffer</code> object * @return the newly created <code>IndexBuffer</code> object
* *
* @exception IllegalStateException if the IndexBuffer could not be created * @exception IllegalStateException if the IndexBuffer could not be created
* @throws RuntimeException if a runtime error occurred, such as running out of memory or other resources, or if a parameter to a builder function was invalid.
* *
* @see #setBuffer * @see #setBuffer
*/ */
@@ -316,6 +316,8 @@ public class IndirectLight {
* @return A newly created <code>IndirectLight</code> * @return A newly created <code>IndirectLight</code>
* *
* @exception IllegalStateException if a parameter to a builder function was invalid. * @exception IllegalStateException if a parameter to a builder function was invalid.
* @throws RuntimeException if a runtime error occurred, such as running out of
* memory or other resources.
*/ */
@NonNull @NonNull
public IndirectLight build(@NonNull Engine engine) { public IndirectLight build(@NonNull Engine engine) {
@@ -781,6 +781,7 @@ public class LightManager {
* *
* @param engine Reference to the {@link Engine} to associate this light with. * @param engine Reference to the {@link Engine} to associate this light with.
* @param entity Entity to add the light component to. * @param entity Entity to add the light component to.
* @throws RuntimeException if a runtime error occurred, such as running out of memory or other resources, or if a parameter to a builder function was invalid.
*/ */
public void build(@NonNull Engine engine, @Entity int entity) { public void build(@NonNull Engine engine, @Entity int entity) {
if (!nBuilderBuild(mNativeBuilder, engine.getNativeObject(), entity)) { if (!nBuilderBuild(mNativeBuilder, engine.getNativeObject(), entity)) {
@@ -476,6 +476,7 @@ public class Material {
* @return the newly created object * @return the newly created object
* *
* @exception IllegalStateException if the material could not be created * @exception IllegalStateException if the material could not be created
* @throws RuntimeException if a parameter to a builder function was invalid.
*/ */
@NonNull @NonNull
public Material build(@NonNull Engine engine) { public Material build(@NonNull Engine engine) {
@@ -156,6 +156,7 @@ public class MaterialInstance {
* *
* @param name the name of the material parameter * @param name the name of the material parameter
* @param x the value of the material parameter * @param x the value of the material parameter
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, boolean x) { public void setParameter(@NonNull String name, boolean x) {
nSetParameterBool(getNativeObject(), name, x); nSetParameterBool(getNativeObject(), name, x);
@@ -166,6 +167,7 @@ public class MaterialInstance {
* *
* @param name the name of the material parameter * @param name the name of the material parameter
* @param x the value of the material parameter * @param x the value of the material parameter
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, float x) { public void setParameter(@NonNull String name, float x) {
nSetParameterFloat(getNativeObject(), name, x); nSetParameterFloat(getNativeObject(), name, x);
@@ -176,6 +178,7 @@ public class MaterialInstance {
* *
* @param name the name of the material parameter * @param name the name of the material parameter
* @param x the value of the material parameter * @param x the value of the material parameter
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, int x) { public void setParameter(@NonNull String name, int x) {
nSetParameterInt(getNativeObject(), name, x); nSetParameterInt(getNativeObject(), name, x);
@@ -187,6 +190,7 @@ public class MaterialInstance {
* @param name the name of the material parameter * @param name the name of the material parameter
* @param x the value of the first component * @param x the value of the first component
* @param y the value of the second component * @param y the value of the second component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, boolean x, boolean y) { public void setParameter(@NonNull String name, boolean x, boolean y) {
nSetParameterBool2(getNativeObject(), name, x, y); nSetParameterBool2(getNativeObject(), name, x, y);
@@ -198,6 +202,7 @@ public class MaterialInstance {
* @param name the name of the material parameter * @param name the name of the material parameter
* @param x the value of the first component * @param x the value of the first component
* @param y the value of the second component * @param y the value of the second component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, float x, float y) { public void setParameter(@NonNull String name, float x, float y) {
nSetParameterFloat2(getNativeObject(), name, x, y); nSetParameterFloat2(getNativeObject(), name, x, y);
@@ -209,6 +214,7 @@ public class MaterialInstance {
* @param name the name of the material parameter * @param name the name of the material parameter
* @param x the value of the first component * @param x the value of the first component
* @param y the value of the second component * @param y the value of the second component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, int x, int y) { public void setParameter(@NonNull String name, int x, int y) {
nSetParameterInt2(getNativeObject(), name, x, y); nSetParameterInt2(getNativeObject(), name, x, y);
@@ -221,6 +227,7 @@ public class MaterialInstance {
* @param x the value of the first component * @param x the value of the first component
* @param y the value of the second component * @param y the value of the second component
* @param z the value of the third component * @param z the value of the third component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, boolean x, boolean y, boolean z) { public void setParameter(@NonNull String name, boolean x, boolean y, boolean z) {
nSetParameterBool3(getNativeObject(), name, x, y, z); nSetParameterBool3(getNativeObject(), name, x, y, z);
@@ -233,6 +240,7 @@ public class MaterialInstance {
* @param x the value of the first component * @param x the value of the first component
* @param y the value of the second component * @param y the value of the second component
* @param z the value of the third component * @param z the value of the third component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, float x, float y, float z) { public void setParameter(@NonNull String name, float x, float y, float z) {
nSetParameterFloat3(getNativeObject(), name, x, y, z); nSetParameterFloat3(getNativeObject(), name, x, y, z);
@@ -245,6 +253,7 @@ public class MaterialInstance {
* @param x the value of the first component * @param x the value of the first component
* @param y the value of the second component * @param y the value of the second component
* @param z the value of the third component * @param z the value of the third component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, int x, int y, int z) { public void setParameter(@NonNull String name, int x, int y, int z) {
nSetParameterInt3(getNativeObject(), name, x, y, z); nSetParameterInt3(getNativeObject(), name, x, y, z);
@@ -258,6 +267,7 @@ public class MaterialInstance {
* @param y the value of the second component * @param y the value of the second component
* @param z the value of the third component * @param z the value of the third component
* @param w the value of the fourth component * @param w the value of the fourth component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, boolean x, boolean y, boolean z, boolean w) { public void setParameter(@NonNull String name, boolean x, boolean y, boolean z, boolean w) {
nSetParameterBool4(getNativeObject(), name, x, y, z, w); nSetParameterBool4(getNativeObject(), name, x, y, z, w);
@@ -271,6 +281,7 @@ public class MaterialInstance {
* @param y the value of the second component * @param y the value of the second component
* @param z the value of the third component * @param z the value of the third component
* @param w the value of the fourth component * @param w the value of the fourth component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, float x, float y, float z, float w) { public void setParameter(@NonNull String name, float x, float y, float z, float w) {
nSetParameterFloat4(getNativeObject(), name, x, y, z, w); nSetParameterFloat4(getNativeObject(), name, x, y, z, w);
@@ -284,6 +295,7 @@ public class MaterialInstance {
* @param y the value of the second component * @param y the value of the second component
* @param z the value of the third component * @param z the value of the third component
* @param w the value of the fourth component * @param w the value of the fourth component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, int x, int y, int z, int w) { public void setParameter(@NonNull String name, int x, int y, int z, int w) {
nSetParameterInt4(getNativeObject(), name, x, y, z, w); nSetParameterInt4(getNativeObject(), name, x, y, z, w);
@@ -299,6 +311,7 @@ public class MaterialInstance {
* @param name The name of the material texture parameter * @param name The name of the material texture parameter
* @param texture The texture to set as parameter * @param texture The texture to set as parameter
* @param sampler The sampler to be used with this texture * @param sampler The sampler to be used with this texture
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, public void setParameter(@NonNull String name,
@NonNull Texture texture, @NonNull TextureSampler sampler) { @NonNull Texture texture, @NonNull TextureSampler sampler) {
@@ -320,6 +333,7 @@ public class MaterialInstance {
* instance.setParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 0, 4); * instance.setParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 0, 4);
* }</pre> * }</pre>
* </p> * </p>
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, public void setParameter(@NonNull String name,
@NonNull BooleanElement type, @NonNull boolean[] v, @NonNull BooleanElement type, @NonNull boolean[] v,
@@ -342,6 +356,7 @@ public class MaterialInstance {
* instance.setParameter("param", MaterialInstance.IntElement.INT4, a, 0, 4); * instance.setParameter("param", MaterialInstance.IntElement.INT4, a, 0, 4);
* }</pre> * }</pre>
* </p> * </p>
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, public void setParameter(@NonNull String name,
@NonNull IntElement type, @NonNull int[] v, @NonNull IntElement type, @NonNull int[] v,
@@ -364,6 +379,7 @@ public class MaterialInstance {
* material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 0, 4); * material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 0, 4);
* }</pre> * }</pre>
* </p> * </p>
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, public void setParameter(@NonNull String name,
@NonNull FloatElement type, @NonNull float[] v, @NonNull FloatElement type, @NonNull float[] v,
@@ -379,6 +395,7 @@ public class MaterialInstance {
* @param r red component * @param r red component
* @param g green component * @param g green component
* @param b blue component * @param b blue component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, @NonNull Colors.RgbType type, public void setParameter(@NonNull String name, @NonNull Colors.RgbType type,
float r, float g, float b) { float r, float g, float b) {
@@ -395,6 +412,7 @@ public class MaterialInstance {
* @param g green component * @param g green component
* @param b blue component * @param b blue component
* @param a alpha component * @param a alpha component
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/ */
public void setParameter(@NonNull String name, @NonNull Colors.RgbaType type, public void setParameter(@NonNull String name, @NonNull Colors.RgbaType type,
float r, float g, float b, float a) { float r, float g, float b, float a) {
@@ -112,6 +112,8 @@ public class MorphTargetBuffer {
* @return the newly created <code>MorphTargetBuffer</code> object * @return the newly created <code>MorphTargetBuffer</code> object
* *
* @exception IllegalStateException if the MorphTargetBuffer could not be created * @exception IllegalStateException if the MorphTargetBuffer could not be created
* @throws RuntimeException if a runtime error occurred, such as running out of
* memory or other resources.
* *
* @see #setMorphTargetBufferOffsetAt * @see #setMorphTargetBufferOffsetAt
*/ */
@@ -587,6 +587,7 @@ public class RenderableManager {
* *
* @param engine reference to the <code>Engine</code> to associate this renderable with * @param engine reference to the <code>Engine</code> to associate this renderable with
* @param entity entity to add the renderable component to * @param entity entity to add the renderable component to
* @throws RuntimeException if a runtime error occurred, such as running out of memory or other resources, or if a parameter to a builder function was invalid.
*/ */
public void build(@NonNull Engine engine, @Entity int entity) { public void build(@NonNull Engine engine, @Entity int entity) {
if (!nBuilderBuild(mNativeBuilder, engine.getNativeObject(), entity)) { if (!nBuilderBuild(mNativeBuilder, engine.getNativeObject(), entity)) {
@@ -269,9 +269,8 @@ public class Renderer {
/** /**
* Set the time at which the frame must be presented to the display. * Set the time at which the frame must be presented to the display.
* <p> *
* This must be called between {@link #beginFrame} and {@link #endFrame}. * This must be called between {@link #beginFrame} and {@link #endFrame}.
* </p>
* *
* @param monotonicClockNanos The time in nanoseconds corresponding to the system monotonic * @param monotonicClockNanos The time in nanoseconds corresponding to the system monotonic
* up-time clock. The presentation time is typically set in the * up-time clock. The presentation time is typically set in the
@@ -354,6 +353,8 @@ public class Renderer {
* returned and produce a frame anyways, by making calls to {@link #render(View)}, * returned and produce a frame anyways, by making calls to {@link #render(View)},
* in which case {@link #endFrame} must be called. * in which case {@link #endFrame} must be called.
* *
* @throws Error if the backend thread encountered an unrecoverable error.
*
* @see #endFrame * @see #endFrame
* @see #render * @see #render
*/ */
@@ -370,6 +371,8 @@ public class Renderer {
* *
* <br><p>All calls to render() must happen <b>before</b> endFrame().</p> * <br><p>All calls to render() must happen <b>before</b> endFrame().</p>
* *
* @throws Error if the backend thread encountered an unrecoverable error, or if called again after a backend exception was already thrown.
*
* @see #beginFrame * @see #beginFrame
* @see #render * @see #render
*/ */
@@ -425,6 +428,7 @@ public class Renderer {
* </ul> * </ul>
* *
* @param view the {@link View} to render * @param view the {@link View} to render
* @throws Error if the backend thread encountered an unrecoverable error, or if called again after a backend exception was already thrown.
* @see #beginFrame * @see #beginFrame
* @see #endFrame * @see #endFrame
* @see View * @see View
@@ -454,6 +458,7 @@ public class Renderer {
* <li><code>renderStandaloneView()</code> performs potentially heavy computations and cannot be * <li><code>renderStandaloneView()</code> performs potentially heavy computations and cannot be
* multi-threaded. However, internally, it is highly multi-threaded to both improve performance * multi-threaded. However, internally, it is highly multi-threaded to both improve performance
* and mitigate the call's latency.</li> * and mitigate the call's latency.</li>
* </ul>
* *
* @param view the {@link View} to render. This View must have an associated {@link RenderTarget} * @param view the {@link View} to render. This View must have an associated {@link RenderTarget}
* @see View * @see View
@@ -519,14 +524,14 @@ public class Renderer {
* <p><code>readPixels</code> must be called within a frame, meaning after {@link #beginFrame} * <p><code>readPixels</code> must be called within a frame, meaning after {@link #beginFrame}
* and before {@link #endFrame}. Typically, <code>readPixels</code> will be called after * and before {@link #endFrame}. Typically, <code>readPixels</code> will be called after
* {@link #render}.</p> * {@link #render}.</p>
* <br> *
* <p>After calling this method, the callback associated with <code>buffer</code> * <p>After issuing this method, the callback associated with <code>buffer</code> will be invoked on the
* will be invoked on the main thread, indicating that the read-back has completed. * main thread, indicating that the read-back has completed. Typically, this will happen
* Typically, this will happen after multiple calls to {@link #beginFrame}, * after multiple calls to {@link #beginFrame}, {@link #render}, {@link #endFrame}.</p>
* {@link #render}, {@link #endFrame}.</p> *
* <br> * <p>It is also possible to use a {@link Fence} to wait for the read-back.</p>
* <p><code>readPixels</code> is intended for debugging and testing. *
* It will impact performance significantly.</p> * <p><code>readPixels</code> is intended for debugging and testing. It will impact performance significantly.</p>
* *
* @param xoffset left offset of the sub-region to read back * @param xoffset left offset of the sub-region to read back
* @param yoffset bottom offset of the sub-region to read back * @param yoffset bottom offset of the sub-region to read back
@@ -604,18 +609,19 @@ public class Renderer {
* *
* <p>Typically <code>readPixels</code> will be called after {@link #render} and before * <p>Typically <code>readPixels</code> will be called after {@link #render} and before
* {@link #endFrame}.</p> * {@link #endFrame}.</p>
* <br> *
* <p>After calling this method, the callback associated with <code>buffer</code> * <p>After issuing this method, the callback associated with <code>buffer</code> will be invoked on the
* will be invoked on the main thread, indicating that the read-back has completed. * main thread, indicating that the read-back has completed. Typically, this will happen
* Typically, this will happen after multiple calls to {@link #beginFrame}, * after multiple calls to {@link #beginFrame}, {@link #render}, {@link #endFrame}.</p>
* {@link #render}, {@link #endFrame}.</p> *
* <br> * <p>It is also possible to use a {@link Fence} to wait for the read-back.</p>
*
* <p>OpenGL only: if issuing a <code>readPixels</code> on a {@link RenderTarget} backed by a * <p>OpenGL only: if issuing a <code>readPixels</code> on a {@link RenderTarget} backed by a
* {@link Texture} that had data uploaded to it via {@link Texture#setImage}, the data returned * {@link Texture} that had data uploaded to it via {@link Texture#setImage}, the data returned
* from <code>readPixels</code> will be y-flipped with respect to the {@link Texture#setImage} * from <code>readPixels</code> will be y-flipped with respect to the {@link Texture#setImage}
* call.</p> * call.</p>
* <p><code>readPixels</code> is intended for debugging and testing. *
* It will impact performance significantly.</p> * <p><code>readPixels</code> is intended for debugging and testing. It will impact performance significantly.</p>
* *
* @param renderTarget {@link RenderTarget} to read back from * @param renderTarget {@link RenderTarget} to read back from
* @param xoffset left offset of the sub-region to read back * @param xoffset left offset of the sub-region to read back
@@ -78,6 +78,8 @@ public class SkinningBuffer {
* @return the newly created <code>SkinningBuffer</code> object * @return the newly created <code>SkinningBuffer</code> object
* *
* @exception IllegalStateException if the SkinningBuffer could not be created * @exception IllegalStateException if the SkinningBuffer could not be created
* @throws RuntimeException if a runtime error occurred, such as running out of
* memory or other resources.
* *
* @see #setBonesAsMatrices * @see #setBonesAsMatrices
* @see #setBonesAsQuaternions * @see #setBonesAsQuaternions
@@ -885,6 +885,8 @@ public class Texture {
* @return A newly created <code>Texture</code> * @return A newly created <code>Texture</code>
* @exception IllegalStateException if a parameter to a builder function was invalid. * @exception IllegalStateException if a parameter to a builder function was invalid.
* A mode detailed message about the error is output in the system log. * A mode detailed message about the error is output in the system log.
* @throws RuntimeException if a runtime error occurred, such as running out of
* memory or other resources.
*/ */
@NonNull @NonNull
public Texture build(@NonNull Engine engine) { public Texture build(@NonNull Engine engine) {
@@ -272,6 +272,8 @@ public class VertexBuffer {
* @return the newly created <code>VertexBuffer</code> object * @return the newly created <code>VertexBuffer</code> object
* *
* @exception IllegalStateException if the VertexBuffer could not be created * @exception IllegalStateException if the VertexBuffer could not be created
* @throws RuntimeException if a runtime error occurred, such as running out of
* memory or other resources.
*/ */
@NonNull @NonNull
public VertexBuffer build(@NonNull Engine engine) { public VertexBuffer build(@NonNull Engine engine) {
@@ -23,6 +23,8 @@
#include <utils/Mutex.h> #include <utils/Mutex.h>
#include <vector> #include <vector>
#include <exception>
#include <atomic>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -75,6 +77,25 @@ public:
bool isExitRequested() const; bool isExitRequested() const;
#ifdef __EXCEPTIONS
bool hasUnrecoverableError() const noexcept {
return mHasUnrecoverableError.load(std::memory_order_acquire);
}
void setUnrecoverableException(std::exception_ptr e) noexcept {
mBackendException = e;
mHasUnrecoverableError.store(true, std::memory_order_release);
}
void propagateBackendException() const;
bool hasExceptionBeenRethrown() const noexcept {
return mExceptionRethrown.load(std::memory_order_relaxed);
}
#else
void propagateBackendException() const noexcept {}
constexpr bool hasUnrecoverableError() const noexcept { return false; }
constexpr bool hasExceptionBeenRethrown() const noexcept { return false; }
#endif
private: private:
const size_t mRequiredSize; const size_t mRequiredSize;
@@ -91,6 +112,12 @@ private:
bool mPaused = false; bool mPaused = false;
static constexpr uint32_t EXIT_REQUESTED = 0x31415926; static constexpr uint32_t EXIT_REQUESTED = 0x31415926;
#ifdef __EXCEPTIONS
mutable std::exception_ptr mBackendException;
std::atomic<bool> mHasUnrecoverableError{false};
mutable std::atomic<bool> mExceptionRethrown{false};
#endif
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -75,6 +75,12 @@ public:
// thread via `purge()`. // thread via `purge()`.
virtual void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback) = 0; virtual void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback) = 0;
/**
* Flags the driver as having encountered an unrecoverable error.
* This will interrupt all pending fence waits and prevent further waits.
*/
virtual void setUnrecoverableError() noexcept {}
virtual ShaderModel getShaderModel() const noexcept = 0; virtual ShaderModel getShaderModel() const noexcept = 0;
// The shader languages used for shaders for this driver in order of preference, used to inform // The shader languages used for shaders for this driver in order of preference, used to inform
@@ -32,6 +32,7 @@
#include <iterator> #include <iterator>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <exception>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -79,8 +80,28 @@ bool CommandBufferQueue::isExitRequested() const {
} }
#ifdef __EXCEPTIONS
void CommandBufferQueue::propagateBackendException() const {
if (UTILS_VERY_UNLIKELY(hasUnrecoverableError())) {
if (!mExceptionRethrown.exchange(true, std::memory_order_relaxed)) {
std::rethrow_exception(mBackendException);
} else {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Engine is in unrecoverable state due to previous backend exception";
}
}
}
#endif
void CommandBufferQueue::flush() { void CommandBufferQueue::flush() {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
#ifdef __EXCEPTIONS
if (UTILS_VERY_UNLIKELY(hasUnrecoverableError())) {
// Drop the current buffer to avoid filling up the circular buffer
mCircularBuffer.getBuffer();
propagateBackendException();
}
#endif
CircularBuffer& circularBuffer = mCircularBuffer; CircularBuffer& circularBuffer = mCircularBuffer;
if (circularBuffer.empty()) { if (circularBuffer.empty()) {
+52 -4
View File
@@ -29,6 +29,7 @@
#include "private/backend/Dispatcher.h" #include "private/backend/Dispatcher.h"
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
#include <atomic>
#include <condition_variable> #include <condition_variable>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -214,18 +215,48 @@ public:
void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback) final; void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback) final;
/**
* Waits for a predicate to become true or until a timeout is reached.
* Returns ERROR if the driver encountered an unrecoverable error.
*/
template<typename Predicate> template<typename Predicate>
bool waitForFence(Predicate predicate, std::chrono::steady_clock::time_point until) { FenceStatus waitForFence(Predicate predicate, std::chrono::steady_clock::time_point until) {
std::unique_lock lock(mFenceMutex); std::unique_lock lock(mFenceMutex);
return mFenceCondition.wait_until(lock, until, predicate); bool errorObserved = false;
bool success = mFenceCondition.wait_until(lock, until, [&]() {
return predicate() ||
(errorObserved = UTILS_VERY_UNLIKELY(mHasUnrecoverableError.load(std::memory_order_relaxed)));
});
if (UTILS_VERY_UNLIKELY(errorObserved)) {
return FenceStatus::ERROR;
}
return success ? FenceStatus::CONDITION_SATISFIED : FenceStatus::TIMEOUT_EXPIRED;
} }
/**
* Waits for a predicate to become true indefinitely.
* Returns ERROR if the driver encountered an unrecoverable error.
*/
template<typename Predicate> template<typename Predicate>
void waitForFence(Predicate predicate) { FenceStatus waitForFence(Predicate predicate) {
std::unique_lock lock(mFenceMutex); std::unique_lock lock(mFenceMutex);
mFenceCondition.wait(lock, predicate); bool errorObserved = false;
mFenceCondition.wait(lock, [&]() {
return predicate() ||
(errorObserved = UTILS_VERY_UNLIKELY(mHasUnrecoverableError.load(std::memory_order_relaxed)));
});
if (UTILS_VERY_UNLIKELY(errorObserved)) {
return FenceStatus::ERROR;
}
return FenceStatus::CONDITION_SATISFIED;
} }
/**
* Executes an action that signals a fence and notifies all waiters.
*/
template<typename Action> template<typename Action>
void signalFence(Action action) { void signalFence(Action action) {
std::lock_guard lock(mFenceMutex); std::lock_guard lock(mFenceMutex);
@@ -233,6 +264,22 @@ public:
mFenceCondition.notify_all(); mFenceCondition.notify_all();
} }
/**
* Flags the driver as having encountered an unrecoverable error.
*/
void setUnrecoverableError() noexcept override {
std::lock_guard lock(mFenceMutex);
mHasUnrecoverableError.store(true, std::memory_order_relaxed);
mFenceCondition.notify_all();
}
/**
* Returns true if the driver has encountered an unrecoverable error.
*/
bool hasUnrecoverableError() const noexcept {
return mHasUnrecoverableError.load(std::memory_order_relaxed);
}
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// Privates // Privates
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@@ -275,6 +322,7 @@ private:
std::condition_variable mFenceCondition; std::condition_variable mFenceCondition;
std::mutex mFenceMutex; std::mutex mFenceMutex;
std::atomic<bool> mHasUnrecoverableError{false};
}; };
+1 -1
View File
@@ -78,7 +78,7 @@ private:
ShaderLanguage preferredLanguage) const noexcept final; ShaderLanguage preferredLanguage) const noexcept final;
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block. // Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
void execute(std::function<void(void)> const& fn) noexcept final; void execute(std::function<void(void)> const& fn) final;
/* /*
* Tasks run regularly on the driver thread. * Tasks run regularly on the driver thread.
+1 -1
View File
@@ -307,7 +307,7 @@ void MetalDriver::setFrameCompletedCallback(
swapChain->setFrameCompletedCallback(handler, std::move(callback)); swapChain->setFrameCompletedCallback(handler, std::move(callback));
} }
void MetalDriver::execute(std::function<void(void)> const& fn) noexcept { void MetalDriver::execute(std::function<void(void)> const& fn) {
@autoreleasepool { @autoreleasepool {
fn(); fn();
} }
+8 -2
View File
@@ -1428,11 +1428,17 @@ FenceStatus MetalFence::wait(uint64_t timeoutNs) {
} }
return false; return false;
}; };
FenceStatus status;
if (timeoutNs == FENCE_WAIT_FOR_EVER) { if (timeoutNs == FENCE_WAIT_FOR_EVER) {
context.driver->waitForFence(predicate); status = context.driver->waitForFence(predicate);
} else { } else {
auto const until = std::chrono::steady_clock::now() + ns(timeoutNs); auto const until = std::chrono::steady_clock::now() + ns(timeoutNs);
context.driver->waitForFence(predicate, until); status = context.driver->waitForFence(predicate, until);
}
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
} }
return result; return result;
} }
+12 -3
View File
@@ -2665,12 +2665,17 @@ FenceStatus OpenGLDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
// so we need to wait for that, before using the real fence. // so we need to wait for that, before using the real fence.
// By construction, "f" can't be destroyed while we wait, because its // By construction, "f" can't be destroyed while we wait, because its
// construction call is in the queue and a destroy call will have to come later. // construction call is in the queue and a destroy call will have to come later.
waitForFence([f] { FenceStatus status = waitForFence([f] {
return f->fence != nullptr; return f->fence != nullptr;
}, until); }, until);
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
if (f->fence == nullptr) { if (f->fence == nullptr) {
// the only possible choice here is that we timed out // the only possible choice here is that we timed out
assert_invariant(f->state->status == FenceStatus::TIMEOUT_EXPIRED); assert_invariant(status == FenceStatus::TIMEOUT_EXPIRED);
return FenceStatus::TIMEOUT_EXPIRED; return FenceStatus::TIMEOUT_EXPIRED;
} }
// here we know that we have the platform fence // here we know that we have the platform fence
@@ -2686,13 +2691,17 @@ FenceStatus OpenGLDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
// This is the case where we need to use OpenGL fences // This is the case where we need to use OpenGL fences
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
FenceStatus result = FenceStatus::TIMEOUT_EXPIRED; FenceStatus result = FenceStatus::TIMEOUT_EXPIRED;
waitForFence([&] { FenceStatus status = waitForFence([&] {
if (f->state->status != FenceStatus::TIMEOUT_EXPIRED) { if (f->state->status != FenceStatus::TIMEOUT_EXPIRED) {
result = f->state->status; result = f->state->status;
return true; return true;
} }
return false; return false;
}, until); }, until);
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
return result; return result;
#else #else
return FenceStatus::ERROR; return FenceStatus::ERROR;
+9 -5
View File
@@ -1492,16 +1492,20 @@ FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout
std::shared_ptr<VulkanCmdFence> cmdfence; std::shared_ptr<VulkanCmdFence> cmdfence;
bool canceled = false; bool canceled = false;
waitForFence([&] { FenceStatus status = waitForFence([&] {
auto status = fence->getStatus(); auto fstatus = fence->getStatus();
if (bool(status.first) || status.second) { if (bool(fstatus.first) || fstatus.second) {
cmdfence = status.first; cmdfence = fstatus.first;
canceled = status.second; canceled = fstatus.second;
return true; return true;
} }
return false; return false;
}, until); }, until);
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
if (!cmdfence || canceled) { if (!cmdfence || canceled) {
return canceled ? FenceStatus::ERROR : FenceStatus::TIMEOUT_EXPIRED; return canceled ? FenceStatus::ERROR : FenceStatus::TIMEOUT_EXPIRED;
} }
+6 -2
View File
@@ -796,12 +796,16 @@ FenceStatus WebGPUDriver::fenceWait(FenceHandle fenceHandle, uint64_t const time
} }
std::shared_ptr<WebGPUSubmissionState> state; std::shared_ptr<WebGPUSubmissionState> state;
bool const success = waitForFence([&] { FenceStatus status = waitForFence([&] {
state = fence->getState(); state = fence->getState();
return bool(state); return bool(state);
}, until); }, until);
if (!success) { if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
if (status == FenceStatus::TIMEOUT_EXPIRED) {
return FenceStatus::TIMEOUT_EXPIRED; return FenceStatus::TIMEOUT_EXPIRED;
} }
+29
View File
@@ -18,6 +18,7 @@
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Skip.h" #include "Skip.h"
#include "../src/DriverBase.h"
using namespace filament; using namespace filament;
using namespace filament::backend; using namespace filament::backend;
@@ -140,4 +141,32 @@ TEST_F(BackendTest, FrameCompletedCallback) {
EXPECT_EQ(callbackCountB, 1); EXPECT_EQ(callbackCountB, 1);
} }
#ifdef __EXCEPTIONS
TEST_F(BackendTest, FenceUnrecoverableErrorInterruption) {
DriverBase* driverBase = static_cast<DriverBase*>(&getDriver());
std::atomic<bool> waitStarted = false;
std::atomic<bool> waitFinished = false;
FenceStatus waitResult = FenceStatus::TIMEOUT_EXPIRED;
std::thread waitingThread([&]() {
waitStarted = true;
waitResult = driverBase->waitForFence([]() { return false; });
waitFinished = true;
});
while (!waitStarted) {
std::this_thread::yield();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
driverBase->setUnrecoverableError();
waitingThread.join();
EXPECT_TRUE(waitFinished);
EXPECT_EQ(waitResult, FenceStatus::ERROR);
}
#endif
} // namespace test } // namespace test
@@ -303,4 +303,37 @@ TEST_F(CommandBufferQueueTest, StressTest) {
} }
#ifdef __EXCEPTIONS
TEST_F(CommandBufferQueueTest, BackendExceptionHandling) {
CommandBufferQueue queue(1024, 4096, false);
// Simulate an exception in the backend thread
std::thread backend([&]() {
try {
std::this_thread::sleep_for(10ms);
throw std::runtime_error("Artificial Backend Failure");
} catch (...) {
queue.setUnrecoverableException(std::current_exception());
}
});
// Wait for backend to fail
std::this_thread::sleep_for(50ms);
// First call to flush should throw the original exception
EXPECT_THROW({
queue.flush();
}, std::runtime_error);
// Subsequent calls should throw due to postcondition check
EXPECT_THROW({
queue.flush();
}, utils::Panic);
backend.join();
}
#endif
} // namespace filament::backend } // namespace filament::backend
+2 -2
View File
@@ -273,7 +273,7 @@ public:
* @param near distance in world units from the camera to the near plane. * @param near distance in world units from the camera to the near plane.
* @param far distance in world units from the camera to the far plane. \p far != \p near. * @param far distance in world units from the camera to the far plane. \p far != \p near.
*/ */
void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept; void setCustomProjection(math::mat4 const& projection, double near, double far);
/** Sets the projection matrix. /** Sets the projection matrix.
* *
@@ -286,7 +286,7 @@ public:
* @param far distance in world units from the camera to the far plane. \p far != \p near. * @param far distance in world units from the camera to the far plane. \p far != \p near.
*/ */
void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling, void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling,
double near, double far) noexcept; double near, double far);
/** Sets a custom projection matrix for each eye. /** Sets a custom projection matrix for each eye.
* *
+18 -1
View File
@@ -782,6 +782,17 @@ public:
*/ */
bool isAsynchronousModeEnabled() const noexcept; bool isAsynchronousModeEnabled() const noexcept;
/**
* Returns whether the engine has encountered an unrecoverable failure.
*
* If this returns true, the engine is in an unrecoverable state and further calls to
* rendering methods will fail or be ignored. Apps can use this to check for fatal
* errors instead of relying on exceptions.
*
* @return true if an unrecoverable failure has occurred, false otherwise.
*/
bool hasUnrecoverableFailure() const noexcept;
/** /**
* Retrieves the configuration settings of this Engine. * Retrieves the configuration settings of this Engine.
* *
@@ -1099,6 +1110,8 @@ public:
* in cases where a guarantee about the <code>SwapChain</code> destruction is needed in a * in cases where a guarantee about the <code>SwapChain</code> destruction is needed in a
* timely fashion, such as when responding to Android's * timely fashion, such as when responding to Android's
* <code>android.view.SurfaceHolder.Callback.surfaceDestroyed</code></p> * <code>android.view.SurfaceHolder.Callback.surfaceDestroyed</code></p>
*
* @note If the backend thread has encountered an unrecoverable error, this function becomes a no-op.
*/ */
void flushAndWait(); void flushAndWait();
@@ -1118,6 +1131,8 @@ public:
* @param timeout A timeout in nanoseconds * @param timeout A timeout in nanoseconds
* @return true if successful, false if flushAndWait timed out, in which case it wasn't successful and commands * @return true if successful, false if flushAndWait timed out, in which case it wasn't successful and commands
* might still be executing on both the CPU and GPU sides. * might still be executing on both the CPU and GPU sides.
*
* @note If the backend thread has encountered an unrecoverable error, this function becomes a no-op and returns false.
*/ */
bool flushAndWait(uint64_t timeout); bool flushAndWait(uint64_t timeout);
@@ -1127,7 +1142,9 @@ public:
* *
* <p>This is typically used after creating a lot of objects to start draining the command * <p>This is typically used after creating a lot of objects to start draining the command
* queue which has a limited size.</p> * queue which has a limited size.</p>
*/ *
* @note If the backend thread has encountered an unrecoverable error, this function becomes a no-op.
*/
void flush(); void flush();
/** /**
+3
View File
@@ -62,6 +62,9 @@ public:
* @return FenceStatus::CONDITION_SATISFIED on success, * @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::TIMEOUT_EXPIRED if the time out expired or * FenceStatus::TIMEOUT_EXPIRED if the time out expired or
* FenceStatus::ERROR in other cases. * FenceStatus::ERROR in other cases.
* @throws std::exception (or derived) if the backend thread encountered an unrecoverable error (when exceptions are enabled and mode is Mode::FLUSH).
* @throws utils::Panic if called again after a backend exception was already thrown.
*
* @see #Mode * @see #Mode
*/ */
FenceStatus wait(Mode mode = Mode::FLUSH, uint64_t timeout = FENCE_WAIT_FOR_EVER); FenceStatus wait(Mode mode = Mode::FLUSH, uint64_t timeout = FENCE_WAIT_FOR_EVER);
+13 -1
View File
@@ -298,9 +298,11 @@ public:
* This is a convenience method that returns the same value as beginFrame(). * This is a convenience method that returns the same value as beginFrame().
* *
* @return * @return
* *false* the current frame should be skipped, * *false* the current frame should be skipped, or an unrecoverable backend exception has occurred.
* *true* the current frame can be rendered * *true* the current frame can be rendered
* *
* @note This method will return false once a backend exception has been delivered to the main thread.
*
* @see * @see
* beginFrame() * beginFrame()
*/ */
@@ -344,6 +346,10 @@ public:
* It is recommended to use the same swapChain for every call to beginFrame, failing to do * It is recommended to use the same swapChain for every call to beginFrame, failing to do
* so can result is losing all or part of the FrameInfo history. * so can result is losing all or part of the FrameInfo history.
* *
* @throws std::exception (or derived) if the backend thread encountered an unrecoverable error (when exceptions are enabled).
*
* @note This method will return false if called again after a backend exception was already thrown and delivered to the main thread.
*
* @see * @see
* endFrame() * endFrame()
*/ */
@@ -414,6 +420,9 @@ public:
* @remark * @remark
* render() is typically called once per frame (but not necessarily). * render() is typically called once per frame (but not necessarily).
* *
* @throws std::exception (or derived) if the backend thread encountered an unrecoverable error (when exceptions are enabled).
* @throws utils::Panic if called again after a backend exception was already thrown.
*
* @see * @see
* beginFrame(), endFrame(), View * beginFrame(), endFrame(), View
* *
@@ -506,6 +515,9 @@ public:
* beginFrame() returned true, otherwise, endFrame() must not be called unless the caller * beginFrame() returned true, otherwise, endFrame() must not be called unless the caller
* ignored beginFrame()'s return value. * ignored beginFrame()'s return value.
* *
* @throws std::exception (or derived) if the backend thread encountered an unrecoverable error (when exceptions are enabled).
* @throws utils::Panic if called again after a backend exception was already thrown.
*
* @see * @see
* beginFrame() * beginFrame()
*/ */
+5 -5
View File
@@ -567,7 +567,7 @@ public:
* @see PlatformCocoaGL::createExternalImage * @see PlatformCocoaGL::createExternalImage
* @see PlatformCocoaTouchGL::createExternalImage * @see PlatformCocoaTouchGL::createExternalImage
*/ */
void setExternalImage(Engine& engine, ExternalImageHandleRef image) noexcept; void setExternalImage(Engine& engine, ExternalImageHandleRef image);
/** /**
* Specify the external image to associate with this Texture. Typically, the external * Specify the external image to associate with this Texture. Typically, the external
@@ -594,7 +594,7 @@ public:
* @deprecated Instead, use setExternalImage(Engine& engine, ExternalImageHandleRef image) * @deprecated Instead, use setExternalImage(Engine& engine, ExternalImageHandleRef image)
*/ */
UTILS_DEPRECATED UTILS_DEPRECATED
void setExternalImage(Engine& engine, void* UTILS_NONNULL image) noexcept; void setExternalImage(Engine& engine, void* UTILS_NONNULL image);
/** /**
* Specify the external image and plane to associate with this Texture. Typically, the external * Specify the external image and plane to associate with this Texture. Typically, the external
@@ -625,7 +625,7 @@ public:
* kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms * kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms
* other than iOS, this method is a no-op. * other than iOS, this method is a no-op.
*/ */
void setExternalImage(Engine& engine, void* UTILS_NONNULL image, size_t plane) noexcept; void setExternalImage(Engine& engine, void* UTILS_NONNULL image, size_t plane);
/** /**
* Specify the external stream to associate with this Texture. Typically, the external * Specify the external stream to associate with this Texture. Typically, the external
@@ -644,7 +644,7 @@ public:
* @see Builder::sampler(), Stream * @see Builder::sampler(), Stream
* *
*/ */
void setExternalStream(Engine& engine, Stream* UTILS_NULLABLE stream) noexcept; void setExternalStream(Engine& engine, Stream* UTILS_NULLABLE stream);
/** /**
* Generates all the mipmap levels automatically. This requires the texture to have a * Generates all the mipmap levels automatically. This requires the texture to have a
@@ -656,7 +656,7 @@ public:
* @attention \p engine must be the instance passed to Builder::build() * @attention \p engine must be the instance passed to Builder::build()
* @attention This Texture instance must NOT use SamplerType::SAMPLER_3D or it has no effect * @attention This Texture instance must NOT use SamplerType::SAMPLER_3D or it has no effect
*/ */
void generateMipmaps(Engine& engine) const noexcept; void generateMipmaps(Engine& engine) const;
/** /**
* This non-blocking method checks if the resource has finished creation. If the resource * This non-blocking method checks if the resource has finished creation. If the resource
+1 -1
View File
@@ -464,7 +464,7 @@ public:
/** /**
* Enables or disables bloom in the post-processing stage. Disabled by default. * Enables or disables bloom in the post-processing stage. Disabled by default.
* *
* @param options options * @param options options. Values may be silently clamped to valid ranges.
*/ */
void setBloomOptions(BloomOptions options) noexcept; void setBloomOptions(BloomOptions options) noexcept;
+2 -2
View File
@@ -81,12 +81,12 @@ void Camera::setProjection(Projection const projection, double const left, doubl
downcast(this)->setProjection(projection, left, right, bottom, top, near, far); downcast(this)->setProjection(projection, left, right, bottom, top, near, far);
} }
void Camera::setCustomProjection(mat4 const& projection, double const near, double const far) noexcept { void Camera::setCustomProjection(mat4 const& projection, double const near, double const far) {
downcast(this)->setCustomProjection(projection, near, far); downcast(this)->setCustomProjection(projection, near, far);
} }
void Camera::setCustomProjection(mat4 const& projection, mat4 const& projectionForCulling, void Camera::setCustomProjection(mat4 const& projection, mat4 const& projectionForCulling,
double const near, double const far) noexcept { double const near, double const far) {
downcast(this)->setCustomProjection(projection, projectionForCulling, near, far); downcast(this)->setCustomProjection(projection, projectionForCulling, near, far);
} }
+4
View File
@@ -474,6 +474,10 @@ bool Engine::isAsynchronousModeEnabled() const noexcept {
return downcast(this)->isAsynchronousModeEnabled(); return downcast(this)->isAsynchronousModeEnabled();
} }
bool Engine::hasUnrecoverableFailure() const noexcept {
return downcast(this)->hasUnrecoverableFailure();
}
size_t Engine::getMaxStereoscopicEyes() noexcept { size_t Engine::getMaxStereoscopicEyes() noexcept {
return FEngine::getMaxStereoscopicEyes(); return FEngine::getMaxStereoscopicEyes();
} }
+5 -5
View File
@@ -68,23 +68,23 @@ backend::AsyncCallId Texture::setImageAsync(Engine& engine, size_t const level,
handler, std::move(callback), user); handler, std::move(callback), user);
} }
void Texture::setExternalImage(Engine& engine, ExternalImageHandleRef image) noexcept { void Texture::setExternalImage(Engine& engine, ExternalImageHandleRef image) {
downcast(this)->setExternalImage(downcast(engine), image); downcast(this)->setExternalImage(downcast(engine), image);
} }
void Texture::setExternalImage(Engine& engine, void* image) noexcept { void Texture::setExternalImage(Engine& engine, void* image) {
downcast(this)->setExternalImage(downcast(engine), image); downcast(this)->setExternalImage(downcast(engine), image);
} }
void Texture::setExternalImage(Engine& engine, void* image, size_t const plane) noexcept { void Texture::setExternalImage(Engine& engine, void* image, size_t const plane) {
downcast(this)->setExternalImage(downcast(engine), image, plane); downcast(this)->setExternalImage(downcast(engine), image, plane);
} }
void Texture::setExternalStream(Engine& engine, Stream* stream) noexcept { void Texture::setExternalStream(Engine& engine, Stream* stream) {
downcast(this)->setExternalStream(downcast(engine), downcast(stream)); downcast(this)->setExternalStream(downcast(engine), downcast(stream));
} }
void Texture::generateMipmaps(Engine& engine) const noexcept { void Texture::generateMipmaps(Engine& engine) const {
downcast(this)->generateMipmaps(downcast(engine)); downcast(this)->generateMipmaps(downcast(engine));
} }
+1 -1
View File
@@ -93,7 +93,7 @@ mat4 FCamera::projection(double const focalLengthInMillimeters,
*/ */
void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& projection, void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& projection,
mat4 const& projectionForCulling, double const near, double const far) noexcept { mat4 const& projectionForCulling, double const near, double const far) {
FILAMENT_CHECK_PRECONDITION(near != far) FILAMENT_CHECK_PRECONDITION(near != far)
<< "Camera preconditions not met in setCustomProjection(): near = far = " << near; << "Camera preconditions not met in setCustomProjection(): near = far = " << near;
+2 -2
View File
@@ -53,10 +53,10 @@ public:
// Sets custom projection matrices (sets both the viewing and culling projections). // Sets custom projection matrices (sets both the viewing and culling projections).
void setCustomProjection(math::mat4 const& projection, void setCustomProjection(math::mat4 const& projection,
math::mat4 const& projectionForCulling, double near, double far) noexcept; math::mat4 const& projectionForCulling, double near, double far);
inline void setCustomProjection(math::mat4 const& projection, inline void setCustomProjection(math::mat4 const& projection,
double const near, double const far) noexcept { double const near, double const far) {
setCustomProjection(projection, projection, near, far); setCustomProjection(projection, projection, near, far);
} }
+81 -7
View File
@@ -91,6 +91,10 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#ifdef __EXCEPTIONS
#include <exception>
#endif
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -831,6 +835,11 @@ void FEngine::submitFrame() {
} }
void FEngine::flush() { void FEngine::flush() {
if (UTILS_VERY_UNLIKELY(mCommandBufferQueue.hasUnrecoverableError())) {
return;
}
propagateBackendException();
// flush the command buffer // flush the command buffer
flushCommandBuffer(mCommandBufferQueue); flushCommandBuffer(mCommandBufferQueue);
@@ -846,6 +855,11 @@ void FEngine::flushAndWait() {
} }
bool FEngine::flushAndWait(uint64_t const timeout) { bool FEngine::flushAndWait(uint64_t const timeout) {
if (UTILS_VERY_UNLIKELY(mCommandBufferQueue.hasUnrecoverableError())) {
return false;
}
propagateBackendException();
FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isPaused()) FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isPaused())
<< "Cannot call Engine::flushAndWait() when rendering thread is paused!"; << "Cannot call Engine::flushAndWait() when rendering thread is paused!";
@@ -954,8 +968,10 @@ int FEngine::loop() {
} }
void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const { void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const {
getDriver().purge(); if (!commandBufferQueue.hasUnrecoverableError()) {
commandBufferQueue.flush(); getDriver().purge();
commandBufferQueue.flush();
}
} }
const FMaterial* FEngine::getSkyboxMaterial() const noexcept { const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
@@ -1594,15 +1610,39 @@ bool FEngine::execute() {
return false; return false;
} }
// execute all command buffers if (UTILS_VERY_UNLIKELY(mCommandBufferQueue.hasUnrecoverableError())) {
auto& driver = getDriverApi(); for (auto& item : buffers) {
for (auto& item : buffers) {
if (UTILS_LIKELY(item.begin)) {
driver.execute(item.begin);
mCommandBufferQueue.releaseBuffer(item); mCommandBufferQueue.releaseBuffer(item);
} }
return true;
} }
// execute all command buffers
auto& driver = getDriverApi();
size_t i = 0;
#ifdef __EXCEPTIONS
try {
#endif
for (; i < buffers.size(); ++i) {
auto& item = buffers[i];
if (UTILS_LIKELY(item.begin)) {
driver.execute(item.begin);
mCommandBufferQueue.releaseBuffer(item);
}
}
#ifdef __EXCEPTIONS
} catch (...) {
mCommandBufferQueue.setUnrecoverableException(std::current_exception());
mDriver->setUnrecoverableError();
setFenceUnrecoverableError();
// Release remaining buffers (including the one that failed)
for (; i < buffers.size(); ++i) {
mCommandBufferQueue.releaseBuffer(buffers[i]);
}
}
#endif
return true; return true;
} }
@@ -1621,6 +1661,40 @@ void FEngine::setPaused(bool const paused) {
mCommandBufferQueue.setPaused(paused); mCommandBufferQueue.setPaused(paused);
} }
void FEngine::setFenceUnrecoverableError() noexcept {
std::lock_guard const lock(mFenceLock);
mFenceHasUnrecoverableError = true;
mFenceCondition.notify_all();
}
void FEngine::signalFence(FenceSignal& signal, FenceSignal::State s) noexcept {
std::lock_guard const lock(mFenceLock);
signal.mState = s;
mFenceCondition.notify_all();
}
Fence::FenceStatus FEngine::waitFence(FenceSignal& signal, uint64_t const timeout) noexcept {
std::unique_lock lock(mFenceLock);
while (signal.mState == FenceSignal::UNSIGNALED && !mFenceHasUnrecoverableError) {
if (timeout == FENCE_WAIT_FOR_EVER) {
mFenceCondition.wait(lock);
} else {
if (timeout == 0 ||
mFenceCondition.wait_for(lock, std::chrono::nanoseconds(timeout)) == std::cv_status::timeout) {
if (mFenceHasUnrecoverableError) break;
return Fence::FenceStatus::TIMEOUT_EXPIRED;
}
}
}
if (UTILS_VERY_UNLIKELY(mFenceHasUnrecoverableError)) {
return Fence::FenceStatus::ERROR;
}
if (signal.mState == FenceSignal::DESTROYED) {
return Fence::FenceStatus::ERROR;
}
return Fence::FenceStatus::CONDITION_SATISFIED;
}
Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept { Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
DriverApi& driver = const_cast<FEngine*>(this)->getDriverApi(); DriverApi& driver = const_cast<FEngine*>(this)->getDriverApi();
return driver.getFeatureLevel(); return driver.getFeatureLevel();
+30
View File
@@ -83,6 +83,7 @@
#include <string_view> #include <string_view>
#include <random> #include <random>
#include <thread> #include <thread>
#include <mutex>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
@@ -138,6 +139,22 @@ public:
using Epoch = clock::time_point; using Epoch = clock::time_point;
using duration = clock::duration; using duration = clock::duration;
#ifdef __EXCEPTIONS
void propagateBackendException() const {
mCommandBufferQueue.propagateBackendException();
}
#else
void propagateBackendException() const noexcept {}
#endif
bool hasExceptionBeenRethrown() const noexcept {
return mCommandBufferQueue.hasExceptionBeenRethrown();
}
bool hasUnrecoverableFailure() const noexcept {
return mCommandBufferQueue.hasUnrecoverableError();
}
public: public:
static Engine* create(Builder const& builder); static Engine* create(Builder const& builder);
@@ -197,6 +214,15 @@ public:
return mActiveFeatureLevel; return mActiveFeatureLevel;
} }
// Sets the fence unrecoverable error state and notifies all waiters.
void setFenceUnrecoverableError() noexcept;
// Signals a fence and notifies all waiters.
void signalFence(FenceSignal& signal, FenceSignal::State s) noexcept;
// Waits for a fence to be signaled or for a timeout.
Fence::FenceStatus waitFence(FenceSignal& signal, uint64_t timeout) noexcept;
size_t getMaxAutomaticInstances() const noexcept { size_t getMaxAutomaticInstances() const noexcept {
return CONFIG_MAX_INSTANCES; return CONFIG_MAX_INSTANCES;
} }
@@ -656,6 +682,10 @@ private:
utils::Mutex mFenceListLock; utils::Mutex mFenceListLock;
ResourceList<FFence> mFences{"Fence"}; ResourceList<FFence> mFences{"Fence"};
mutable utils::Mutex mFenceLock;
mutable utils::Condition mFenceCondition;
bool mFenceHasUnrecoverableError = false;
// the sync list is accessed from multiple threads, because they are // the sync list is accessed from multiple threads, because they are
// synchronization objects. // synchronization objects.
utils::Mutex mSyncListLock; utils::Mutex mSyncListLock;
+7 -32
View File
@@ -37,8 +37,6 @@ namespace filament {
using namespace backend; using namespace backend;
utils::Mutex FFence::sLock;
utils::Condition FFence::sCondition;
static constexpr uint64_t PUMP_INTERVAL_MILLISECONDS = 1; static constexpr uint64_t PUMP_INTERVAL_MILLISECONDS = 1;
@@ -51,14 +49,15 @@ FFence::FFence(FEngine& engine)
// we have to first wait for the fence to be signaled by the command stream // we have to first wait for the fence to be signaled by the command stream
auto& fs = mFenceSignal; auto& fs = mFenceSignal;
driverApi.queueCommand([fs]() { FEngine* const pEngine = &engine;
fs->signal(); driverApi.queueCommand([fs, pEngine]() {
pEngine->signalFence(*fs, FenceSignal::SIGNALED);
}); });
} }
void FFence::terminate(FEngine&) noexcept { void FFence::terminate(FEngine& engine) noexcept {
FenceSignal * const fs = mFenceSignal.get(); FenceSignal * const fs = mFenceSignal.get();
fs->signal(FenceSignal::DESTROYED); engine.signalFence(*fs, FenceSignal::DESTROYED);
} }
UTILS_NOINLINE UTILS_NOINLINE
@@ -85,14 +84,14 @@ FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) {
FenceStatus status; FenceStatus status;
if (UTILS_LIKELY(!engine.pumpPlatformEvents())) { if (UTILS_LIKELY(!engine.pumpPlatformEvents())) {
status = fs->wait(timeout); status = engine.waitFence(*fs, timeout);
} else { } else {
// Unfortunately, some platforms might force us to have sync points between the GL thread // Unfortunately, some platforms might force us to have sync points between the GL thread
// and user thread. To prevent deadlock on these platforms, we chop up the waiting time into // and user thread. To prevent deadlock on these platforms, we chop up the waiting time into
// polling and pumping the platform's event queue. // polling and pumping the platform's event queue.
const auto startTime = std::chrono::system_clock::now(); const auto startTime = std::chrono::system_clock::now();
while (true) { while (true) {
status = fs->wait(ns(ms(PUMP_INTERVAL_MILLISECONDS)).count()); status = engine.waitFence(*fs, ns(ms(PUMP_INTERVAL_MILLISECONDS)).count());
if (status != FenceStatus::TIMEOUT_EXPIRED) { if (status != FenceStatus::TIMEOUT_EXPIRED) {
break; break;
} }
@@ -111,30 +110,6 @@ FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) {
return status; return status;
} }
UTILS_NOINLINE
void FFence::FenceSignal::signal(State const s) noexcept {
std::lock_guard const lock(sLock);
mState = s;
sCondition.notify_all();
}
UTILS_NOINLINE
Fence::FenceStatus FFence::FenceSignal::wait(uint64_t const timeout) noexcept {
std::unique_lock lock(sLock);
while (mState == UNSIGNALED) {
if (timeout == FENCE_WAIT_FOR_EVER) {
sCondition.wait(lock);
} else {
if (timeout == 0 ||
sCondition.wait_for(lock, ns(timeout)) == std::cv_status::timeout) {
return FenceStatus::TIMEOUT_EXPIRED;
}
}
}
if (mState == DESTROYED) {
return FenceStatus::ERROR;
}
return FenceStatus::CONDITION_SATISFIED;
}
} // namespace filament } // namespace filament
+5 -13
View File
@@ -31,6 +31,11 @@ namespace filament {
class FEngine; class FEngine;
struct FenceSignal {
enum State : uint8_t { UNSIGNALED, SIGNALED, DESTROYED };
State mState = UNSIGNALED;
};
class FFence : public Fence { class FFence : public Fence {
public: public:
FFence(FEngine& engine); FFence(FEngine& engine);
@@ -42,19 +47,6 @@ public:
static FenceStatus waitAndDestroy(FFence* fence, Mode mode) noexcept; static FenceStatus waitAndDestroy(FFence* fence, Mode mode) noexcept;
private: private:
// We assume we don't have a lot of contention of fence and have all of them
// share a single lock/condition
static utils::Mutex sLock;
static utils::Condition sCondition;
struct FenceSignal {
explicit FenceSignal() noexcept = default;
enum State : uint8_t { UNSIGNALED, SIGNALED, DESTROYED };
State mState = UNSIGNALED;
void signal(State s = SIGNALED) noexcept;
FenceStatus wait(uint64_t timeout) noexcept;
};
FEngine& mEngine; FEngine& mEngine;
// TODO: use custom allocator for these small objects // TODO: use custom allocator for these small objects
std::shared_ptr<FenceSignal> mFenceSignal; std::shared_ptr<FenceSignal> mFenceSignal;
+1 -1
View File
@@ -117,7 +117,7 @@ void FInstanceBuffer::setLocalTransforms(
memcpy(mLocalTransforms.data() + offset, localTransforms, sizeof(math::mat4f) * count); memcpy(mLocalTransforms.data() + offset, localTransforms, sizeof(math::mat4f) * count);
} }
math::mat4f const& FInstanceBuffer::getLocalTransform(size_t index) const noexcept { math::mat4f const& FInstanceBuffer::getLocalTransform(size_t index) const {
FILAMENT_CHECK_PRECONDITION(index < mInstanceCount) FILAMENT_CHECK_PRECONDITION(index < mInstanceCount)
<< "getLocalTransform overflow: 'index (" << index << "getLocalTransform overflow: 'index (" << index
<< ") must be < getInstanceCount() ("<< mInstanceCount << ")."; << ") must be < getInstanceCount() ("<< mInstanceCount << ").";
+1 -1
View File
@@ -49,7 +49,7 @@ public:
void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset); void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset);
math::mat4f const& getLocalTransform(size_t index) const noexcept; math::mat4f const& getLocalTransform(size_t index) const;
void prepare( void prepare(
PerRenderableData* buffer, uint32_t index, uint32_t count, PerRenderableData* buffer, uint32_t index, uint32_t count,
+18 -13
View File
@@ -216,6 +216,24 @@ void FMaterialInstance::commit(FEngine& engine) const {
} }
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const { void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
if (!mTextureParameters.empty()) {
FEngine const& engine = mMaterial->getEngine();
// First pass: check preconditions
for (auto const& [binding, p]: mTextureParameters) {
assert_invariant(p.texture);
FILAMENT_CHECK_PRECONDITION(engine.isValid(p.texture))
<< "Invalid texture still bound to MaterialInstance: '" << getName() << "'\n";
}
// Second pass: update state
for (auto const& [binding, p]: mTextureParameters) {
Handle<HwTexture> const handle = p.texture->getHwHandleForSampling();
assert_invariant(handle);
mDescriptorSet.setSampler(mMaterial->getDescriptorSetLayout(),
binding, handle, p.params);
}
}
if (mUniforms.isDirty()) { if (mUniforms.isDirty()) {
mUniforms.clean(); mUniforms.clean();
if (isUsingUboBatching()) { if (isUsingUboBatching()) {
@@ -232,19 +250,6 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
driver.updateBufferObject(*ubHandle, mUniforms.toBufferDescriptor(driver), 0); driver.updateBufferObject(*ubHandle, mUniforms.toBufferDescriptor(driver), 0);
} }
} }
if (!mTextureParameters.empty()) {
for (auto const& [binding, p]: mTextureParameters) {
assert_invariant(p.texture);
// TODO: figure out a way to do this more efficiently (isValid() is a hashmap lookup)
FEngine const& engine = mMaterial->getEngine();
FILAMENT_CHECK_PRECONDITION(engine.isValid(p.texture))
<< "Invalid texture still bound to MaterialInstance: '" << getName() << "'\n";
Handle<HwTexture> const handle = p.texture->getHwHandleForSampling();
assert_invariant(handle);
mDescriptorSet.setSampler(mMaterial->getDescriptorSetLayout(),
binding, handle, p.params);
}
}
// TODO: eventually we should remove this in RELEASE builds // TODO: eventually we should remove this in RELEASE builds
fixMissingSamplers(); fixMissingSamplers();
+14 -1
View File
@@ -316,6 +316,9 @@ void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
} }
bool FRenderer::shouldRenderFrame() const noexcept { bool FRenderer::shouldRenderFrame() const noexcept {
if (UTILS_VERY_UNLIKELY(mEngine.hasExceptionBeenRethrown())) {
return false;
}
FEngine& engine = mEngine; FEngine& engine = mEngine;
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
bool const renderFrame = mFrameSkipper.shouldRenderFrame(driver); bool const renderFrame = mFrameSkipper.shouldRenderFrame(driver);
@@ -365,10 +368,16 @@ bool FRenderer::shouldRenderFrame() const noexcept {
} }
bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) { bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
assert_invariant(swapChain); FILAMENT_CHECK_PRECONDITION(swapChain) << "swapChain cannot be null.";
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT, "frameId", (mFrameId + 1)); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT, "frameId", (mFrameId + 1));
if (UTILS_VERY_UNLIKELY(mEngine.hasExceptionBeenRethrown())) {
return false;
}
mEngine.propagateBackendException();
if (!vsyncSteadyClockTimeNano) { if (!vsyncSteadyClockTimeNano) {
vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano; vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano;
mVsyncSteadyClockTimeNano = 0; mVsyncSteadyClockTimeNano = 0;
@@ -469,6 +478,8 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
void FRenderer::endFrame() { void FRenderer::endFrame() {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
mEngine.propagateBackendException();
if (UTILS_UNLIKELY(mBeginFrameInternal)) { if (UTILS_UNLIKELY(mBeginFrameInternal)) {
mBeginFrameInternal(); mBeginFrameInternal();
mBeginFrameInternal = {}; mBeginFrameInternal = {};
@@ -637,6 +648,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
void FRenderer::render(FView const* view) { void FRenderer::render(FView const* view) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
mEngine.propagateBackendException();
if (UTILS_UNLIKELY(mBeginFrameInternal)) { if (UTILS_UNLIKELY(mBeginFrameInternal)) {
// This is unlikely to happen, the user should not call render() if we returned false from // This is unlikely to happen, the user should not call render() if we returned false from
// beginFrame(). But because this is allowed, we handle it gracefully. // beginFrame(). But because this is allowed, we handle it gracefully.
+7 -7
View File
@@ -656,7 +656,7 @@ AsyncCallId FTexture::setImageAsync(FEngine& engine, size_t const level,
return id; return id;
} }
void FTexture::setExternalImage(FEngine& engine, ExternalImageHandleRef image) noexcept { void FTexture::setExternalImage(FEngine& engine, ExternalImageHandleRef image) {
FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external.";
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading"; << "Texture is not created yet. It may be in the process of asynchronous loading";
@@ -678,7 +678,7 @@ void FTexture::setExternalImage(FEngine& engine, ExternalImageHandleRef image) n
setHandles(texture); setHandles(texture);
} }
void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { void FTexture::setExternalImage(FEngine& engine, void* image) {
FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external.";
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading"; << "Texture is not created yet. It may be in the process of asynchronous loading";
@@ -700,7 +700,7 @@ void FTexture::setExternalImage(FEngine& engine, void* image) noexcept {
setHandles(texture); setHandles(texture);
} }
void FTexture::setExternalImage(FEngine& engine, void* image, size_t const plane) noexcept { void FTexture::setExternalImage(FEngine& engine, void* image, size_t const plane) {
FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external.";
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading"; << "Texture is not created yet. It may be in the process of asynchronous loading";
@@ -723,7 +723,7 @@ void FTexture::setExternalImage(FEngine& engine, void* image, size_t const plane
setHandles(texture); setHandles(texture);
} }
void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept { void FTexture::setExternalStream(FEngine& engine, FStream* stream) {
FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external.";
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading"; << "Texture is not created yet. It may be in the process of asynchronous loading";
@@ -750,7 +750,7 @@ void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept {
} }
} }
void FTexture::generateMipmaps(FEngine& engine) const noexcept { void FTexture::generateMipmaps(FEngine& engine) const {
FILAMENT_CHECK_PRECONDITION(!mExternal) FILAMENT_CHECK_PRECONDITION(!mExternal)
<< "External Textures are not mipmappable."; << "External Textures are not mipmappable.";
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
@@ -831,14 +831,14 @@ Handle<HwTexture> FTexture::createPlaceholderTexture(
return handle; return handle;
} }
backend::Handle<backend::HwTexture> FTexture::getHwHandle() const noexcept { backend::Handle<backend::HwTexture> FTexture::getHwHandle() const {
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading"; << "Texture is not created yet. It may be in the process of asynchronous loading";
return mHandle; return mHandle;
} }
Handle<HwTexture> FTexture::getHwHandleForSampling() const noexcept { Handle<HwTexture> FTexture::getHwHandleForSampling() const {
FILAMENT_CHECK_PRECONDITION(isCreationComplete()) FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading"; << "Texture is not created yet. It may be in the process of asynchronous loading";
+7 -7
View File
@@ -47,8 +47,8 @@ public:
// frees driver resources, object becomes invalid // frees driver resources, object becomes invalid
void terminate(FEngine& engine); void terminate(FEngine& engine);
backend::Handle<backend::HwTexture> getHwHandle() const noexcept; backend::Handle<backend::HwTexture> getHwHandle() const;
backend::Handle<backend::HwTexture> getHwHandleForSampling() const noexcept; backend::Handle<backend::HwTexture> getHwHandleForSampling() const;
size_t getWidth(size_t level = 0) const noexcept; size_t getWidth(size_t level = 0) const noexcept;
size_t getHeight(size_t level = 0) const noexcept; size_t getHeight(size_t level = 0) const noexcept;
@@ -73,12 +73,12 @@ public:
PixelBufferDescriptor&& buffer, backend::CallbackHandler* handler, PixelBufferDescriptor&& buffer, backend::CallbackHandler* handler,
AsyncCompletionCallback callback, void* user) const; AsyncCompletionCallback callback, void* user) const;
void setExternalImage(FEngine& engine, ExternalImageHandleRef image) noexcept; void setExternalImage(FEngine& engine, ExternalImageHandleRef image);
void setExternalImage(FEngine& engine, void* image) noexcept; void setExternalImage(FEngine& engine, void* image);
void setExternalImage(FEngine& engine, void* image, size_t plane) noexcept; void setExternalImage(FEngine& engine, void* image, size_t plane);
void setExternalStream(FEngine& engine, FStream* stream) noexcept; void setExternalStream(FEngine& engine, FStream* stream);
void generateMipmaps(FEngine& engine) const noexcept; void generateMipmaps(FEngine& engine) const;
bool isCompressed() const noexcept { return isCompressedFormat(mFormat); } bool isCompressed() const noexcept { return isCompressedFormat(mFormat); }
+1 -1
View File
@@ -38,7 +38,7 @@ const char* FrameGraphResources::getPassName() const noexcept {
// this perhaps weirdly returns a reference, this is to express the fact that if this method // this perhaps weirdly returns a reference, this is to express the fact that if this method
// fails, it has to assert (or throw), it can't return for e.g. a nullptr, because the public // fails, it has to assert (or throw), it can't return for e.g. a nullptr, because the public
// API doesn't return pointers. // API doesn't return pointers.
// We still use FILAMENT_CHECK_PRECONDITION() because these failures are due to post conditions not met. // We use FILAMENT_CHECK_PRECONDITION() because these failures are due to caller contract violations (preconditions).
VirtualResource& FrameGraphResources::getResource(FrameGraphHandle const handle) const { VirtualResource& FrameGraphResources::getResource(FrameGraphHandle const handle) const {
FILAMENT_CHECK_PRECONDITION(handle) << "Uninitialized handle when using FrameGraphResources."; FILAMENT_CHECK_PRECONDITION(handle) << "Uninitialized handle when using FrameGraphResources.";
+20 -3
View File
@@ -78,6 +78,10 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#ifdef __EXCEPTIONS
#include <exception>
#endif
#include <stdint.h> #include <stdint.h>
#include "generated/resources/filamentapp.h" #include "generated/resources/filamentapp.h"
@@ -248,6 +252,9 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
SDL_Window* sdlWindow = window->getSDLWindow(); SDL_Window* sdlWindow = window->getSDLWindow();
#ifdef __EXCEPTIONS
try {
#endif
while (!mClosed) { while (!mClosed) {
if (mWindowTitle != SDL_GetWindowTitle(sdlWindow)) { if (mWindowTitle != SDL_GetWindowTitle(sdlWindow)) {
SDL_SetWindowTitle(sdlWindow, mWindowTitle.c_str()); SDL_SetWindowTitle(sdlWindow, mWindowTitle.c_str());
@@ -262,7 +269,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
cameraFocalLength = mCameraFocalLength; cameraFocalLength = mCameraFocalLength;
cameraNear = mCameraNear; cameraNear = mCameraNear;
cameraFar = mCameraFar; cameraFar = mCameraFar;
} }
if (!UTILS_HAS_THREADING) { if (!UTILS_HAS_THREADING) {
mEngine->execute(); mEngine->execute();
@@ -371,10 +378,10 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
break; break;
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
switch (event.window.event) { switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_RESIZED:
window->resize(); window->resize();
break; break;
default: default:
break; break;
} }
break; break;
@@ -562,6 +569,16 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
++mSkippedFrames; ++mSkippedFrames;
} }
} }
#ifdef __EXCEPTIONS
} catch (Panic const& e) {
LOG(ERROR) << "Filament exception (terminate cleanly): " << e.what();
LOG(ERROR) << e.getCallStack();
} catch (std::exception const& e) {
LOG(ERROR) << "System exception (terminate cleanly): " << e.what();
} catch (...) {
LOG(ERROR) << "Unknown exception! (terminate cleanly)";
}
#endif
if (mImGuiHelper) { if (mImGuiHelper) {
mImGuiHelper.reset(); mImGuiHelper.reset();
+6 -1
View File
@@ -35,6 +35,7 @@
#ifdef __EXCEPTIONS #ifdef __EXCEPTIONS
# define UTILS_EXCEPTIONS 1 # define UTILS_EXCEPTIONS 1
# include <exception>
#else #else
# ifdef UTILS_EXCEPTIONS # ifdef UTILS_EXCEPTIONS
# error UTILS_EXCEPTIONS is already defined! # error UTILS_EXCEPTIONS is already defined!
@@ -262,7 +263,11 @@ namespace utils {
* The Panic class provides the std::exception protocol, it is the base exception object * The Panic class provides the std::exception protocol, it is the base exception object
* used for all thrown exceptions. * used for all thrown exceptions.
*/ */
class UTILS_PUBLIC Panic { class UTILS_PUBLIC Panic
#ifdef __EXCEPTIONS
: public std::exception
#endif
{
public: public:
using PanicHandlerCallback = void(*)(void* user, Panic const& panic); using PanicHandlerCallback = void(*)(void* user, Panic const& panic);
+6 -4
View File
@@ -207,8 +207,7 @@ void TPanic<T>::panic(char const* function, char const* file, int const line, ch
} }
template<typename T> template<typename T>
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal, void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal, CString reason) {
CString reason) {
if (reason.empty()) { if (reason.empty()) {
reason = CString(literal); reason = CString(literal);
@@ -216,15 +215,18 @@ void TPanic<T>::panic(char const* function, char const* file, int line, char con
T e(function, formatFile(file), line, literal, std::move(reason)); T e(function, formatFile(file), line, literal, std::move(reason));
// always log the Panic at the point it is detected #ifndef __EXCEPTIONS
// log the Panic at the point it is detected unless we have exceptions, the exception handler will be
// responsible for that.
e.log(); e.log();
#endif
// Call the user provided handler // Call the user provided handler
UserPanicHandler::get().call(e); UserPanicHandler::get().call(e);
// if exceptions are enabled, throw now. // if exceptions are enabled, throw now.
#ifdef __EXCEPTIONS #ifdef __EXCEPTIONS
throw e; throw std::move(e);
#endif #endif
// and finally abort if we somehow get here // and finally abort if we somehow get here
+2
View File
@@ -450,6 +450,8 @@ class_<Engine>("Engine")
.function("isAutomaticInstancingEnabled", &Engine::isAutomaticInstancingEnabled) .function("isAutomaticInstancingEnabled", &Engine::isAutomaticInstancingEnabled)
.function("hasUnrecoverableFailure", &Engine::hasUnrecoverableFailure)
.function("getSupportedFeatureLevel", &Engine::getSupportedFeatureLevel) .function("getSupportedFeatureLevel", &Engine::getSupportedFeatureLevel)
.function("setActiveFeatureLevel", &Engine::setActiveFeatureLevel) .function("setActiveFeatureLevel", &Engine::setActiveFeatureLevel)