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;
}
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*,
+3
View File
@@ -48,6 +48,9 @@ struct JniCallback : private filament::backend::CallbackHandler {
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(JniCallback* callback);
// destroy ourselves without executing the callback
static void destroy(JniCallback* callback);
// CallbackHandler interface.
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/CallbackUtils.cpp
../common/NioUtils.cpp
../common/JniUtils.cpp
)
target_include_directories(filament-jni PRIVATE
@@ -16,7 +16,6 @@
#include <jni.h>
#include <functional>
#include <stdlib.h>
#include <string.h>
@@ -26,6 +25,7 @@
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament;
using namespace backend;
@@ -63,7 +63,9 @@ Java_com_google_android_filament_BufferObject_nBuilderBuild(JNIEnv *env, jclass
jlong nativeBuilder, jlong nativeEngine) {
BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder;
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
@@ -81,20 +83,22 @@ Java_com_google_android_filament_BufferObject_nSetBuffer(JNIEnv *env, jclass typ
BufferObject *bufferObject = (BufferObject *) nativeBufferObject;
Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, buffer, count);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
return filament::android::wrapJni<int>(env, [=]() {
AutoBuffer nioBuffer(env, buffer, count);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// 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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
BufferDescriptor desc(data, sizeInBytes,
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 <filament/Camera.h>
#include <common/JniUtils.h>
#include <utils/Entity.h>
@@ -24,21 +25,26 @@
#include <math/mat4.h>
using namespace filament;
using namespace filament::android;
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,
jdouble far) {
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
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,
jint fov) {
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
@@ -49,10 +55,12 @@ Java_com_google_android_filament_Camera_nGetFieldOfViewInDegrees(JNIEnv*, jclass
}
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) {
Camera *camera = (Camera *) nativeCamera;
camera->setLensProjection(focalLength, aspect, near, far);
wrapJni(env, [=]() {
camera->setLensProjection(focalLength, aspect, near, far);
});
}
extern "C" JNIEXPORT void JNICALL
@@ -62,10 +70,12 @@ Java_com_google_android_filament_Camera_nSetCustomProjection(JNIEnv *env, jclass
Camera *camera = (Camera *) nativeCamera;
jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL);
jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL);
camera->setCustomProjection(
*reinterpret_cast<const filament::math::mat4 *>(inProjection),
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
near, far);
wrapJni(env, [=]() {
camera->setCustomProjection(
*reinterpret_cast<const filament::math::mat4 *>(inProjection),
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
near, far);
});
env->ReleaseDoubleArrayElements(inProjection_, inProjection, 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;
jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL);
jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL);
camera->setCustomEyeProjection(
reinterpret_cast<const filament::math::mat4 *>(inProjection), (size_t) count,
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
near, far);
wrapJni(env, [=]() {
camera->setCustomEyeProjection(
reinterpret_cast<const filament::math::mat4 *>(inProjection), (size_t) count,
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
near, far);
});
env->ReleaseDoubleArrayElements(inProjection_, inProjection, 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_) {
Camera* camera = (Camera *) nativeCamera;
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);
}
@@ -21,6 +21,7 @@
#include <math/vec3.h>
#include <math/vec4.h>
#include <common/JniUtils.h>
using namespace filament;
using namespace math;
@@ -37,10 +38,12 @@ Java_com_google_android_filament_ColorGrading_nDestroyBuilder(JNIEnv*, jclass, j
}
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;
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
+131 -62
View File
@@ -16,6 +16,8 @@
#include <jni.h>
#include <exception>
#include <filament/Camera.h>
#include <filament/Engine.h>
#include <filament/MorphTargetBuffer.h>
@@ -28,14 +30,18 @@
#include <filament/View.h>
#include "common/CallbackUtils.h"
#include "common/JniUtils.h"
using namespace filament;
using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
Engine::destroy(&engine);
Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv *env, jclass, jlong nativeEngine) {
wrapJni(env, [=]() {
Engine* engine = (Engine*) nativeEngine;
Engine::destroy(&engine);
});
}
// SwapChain
@@ -77,11 +83,13 @@ Java_com_google_android_filament_Engine_nCreateSwapChainFromRawPointer(JNIEnv*,
}
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) {
Engine* engine = (Engine*) nativeEngine;
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
return engine->destroy(swapChain);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(swapChain);
});
}
// View
@@ -94,11 +102,13 @@ Java_com_google_android_filament_Engine_nCreateView(JNIEnv*, jclass,
}
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) {
Engine* engine = (Engine*) nativeEngine;
View* view = (View*) nativeView;
return engine->destroy(view);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(view);
});
}
// Renderer
@@ -111,11 +121,13 @@ Java_com_google_android_filament_Engine_nCreateRenderer(JNIEnv*, jclass,
}
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) {
Engine* engine = (Engine*) nativeEngine;
Renderer* renderer = (Renderer*) nativeRenderer;
return engine->destroy(renderer);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(renderer);
});
}
// Camera
@@ -137,11 +149,13 @@ Java_com_google_android_filament_Engine_nGetCameraComponent(JNIEnv*, jclass,
}
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_) {
Engine* engine = (Engine*) nativeEngine;
Entity& entity = *reinterpret_cast<Entity*>(&entity_);
engine->destroyCameraComponent(entity);
wrapJni(env, [=]() {
Engine* engine = (Engine*) nativeEngine;
Entity entity = *reinterpret_cast<const Entity*>(&entity_);
engine->destroyCameraComponent(entity);
});
}
// Scene
@@ -154,11 +168,13 @@ Java_com_google_android_filament_Engine_nCreateScene(JNIEnv*, jclass,
}
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) {
Engine* engine = (Engine*) nativeEngine;
Scene* scene = (Scene*) nativeScene;
return engine->destroy(scene);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(scene);
});
}
// Fence
@@ -171,119 +187,147 @@ Java_com_google_android_filament_Engine_nCreateFence(JNIEnv*, jclass,
}
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) {
Engine* engine = (Engine*) nativeEngine;
Fence* fence = (Fence*) nativeFence;
return engine->destroy(fence);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(fence);
});
}
// Stream
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) {
Engine* engine = (Engine*) nativeEngine;
Stream* stream = (Stream*) nativeStream;
return engine->destroy(stream);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(stream);
});
}
// Others...
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) {
Engine* engine = (Engine*) nativeEngine;
IndexBuffer* indexBuffer = (IndexBuffer*) nativeIndexBuffer;
return engine->destroy(indexBuffer);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(indexBuffer);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
VertexBuffer* vertexBuffer = (VertexBuffer*) nativeVertexBuffer;
return engine->destroy(vertexBuffer);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(vertexBuffer);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
SkinningBuffer* skinningBuffer = (SkinningBuffer*) nativeSkinningBuffer;
return engine->destroy(skinningBuffer);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(skinningBuffer);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
MorphTargetBuffer* mtb = (MorphTargetBuffer*) nativeMorphTargetBuffer;
return engine->destroy(mtb);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(mtb);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
IndirectLight* indirectLight = (IndirectLight*) nativeIndirectLight;
return engine->destroy(indirectLight);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(indirectLight);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
Material* material = (Material*) nativeMaterial;
return engine->destroy(material);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(material);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
MaterialInstance* materialInstance = (MaterialInstance*) nativeMaterialInstance;
return engine->destroy(materialInstance);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(materialInstance);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
Skybox* skybox = (Skybox*) nativeSkybox;
return engine->destroy(skybox);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(skybox);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
ColorGrading* colorGrading = (ColorGrading*) nativeColorGrading;
return engine->destroy(colorGrading);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(colorGrading);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
Texture* texture = (Texture*) nativeTexture;
return engine->destroy(texture);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(texture);
});
}
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) {
Engine* engine = (Engine*) nativeEngine;
RenderTarget* target = (RenderTarget*) nativeTarget;
return engine->destroy(target);
return wrapJni<jboolean>(env, [=]() {
return engine->destroy(target);
});
}
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_) {
Engine* engine = (Engine*) nativeEngine;
Entity& entity = *reinterpret_cast<Entity*>(&entity_);
engine->destroy(entity);
wrapJni(env, [=]() {
Engine* engine = (Engine*) nativeEngine;
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
Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass,
Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv *env, jclass,
jlong nativeEngine, jlong timeout) {
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
Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
Java_com_google_android_filament_Engine_nFlush(JNIEnv *env, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
engine->flush();
wrapJni(env, [=]() {
engine->flush();
});
}
extern "C" JNIEXPORT jboolean JNICALL
@@ -436,21 +484,32 @@ Java_com_google_android_filament_Engine_nIsPaused(JNIEnv*, jclass,
}
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,
jint shadowReceiver, jint skinning, jobject handler, jobject runnable) {
Engine* engine = (Engine*) nativeEngine;
Material* material = (Material*) nativeMaterial;
View* view = (View*) nativeView;
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
engine->compile(
(backend::CompilerPriorityQueue) priority,
material, view,
(utils::tribool::value_t) shadowReceiver,
(utils::tribool::value_t) skinning,
jniCallback->getHandler(), [jniCallback](Material*){
JniCallback::postToJavaAndDestroy(jniCallback);
});
wrapJni(env, [=]() {
#if defined(__EXCEPTIONS)
try {
#endif
engine->compile(
(backend::CompilerPriorityQueue) priority,
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
@@ -511,6 +570,12 @@ Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, j
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
Java_com_google_android_filament_Engine_nGetMaxStereoscopicEyes(JNIEnv*, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
@@ -526,10 +591,12 @@ Java_com_google_android_filament_Engine_nGetSupportedFeatureLevel(JNIEnv *, jcla
}
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) {
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
@@ -651,9 +718,11 @@ Java_com_google_android_filament_Engine_nSetBuilderFeature(JNIEnv *env, jclass c
}
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;
return (jlong) builder->build();
return wrapJniBackend<jlong>(env, [=]() {
return (jlong) builder->build();
});
}
extern "C"
@@ -17,6 +17,7 @@
#include <jni.h>
#include <filament/Fence.h>
#include <common/JniUtils.h>
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,
jlong timeoutNanoSeconds) {
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
Java_com_google_android_filament_Fence_nWaitAndDestroy(JNIEnv *env, jclass type, jlong nativeFence,
jint mode) {
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 <functional>
#include <stdlib.h>
#include <string.h>
#include <filament/IndexBuffer.h>
#include <common/JniUtils.h>
#include <backend/BufferDescriptor.h>
@@ -29,6 +29,7 @@
using namespace filament;
using namespace backend;
using namespace filament::android;
extern "C" JNIEXPORT jlong JNICALL
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) {
IndexBuffer::Builder* builder = (IndexBuffer::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine);
return wrapJni<jlong>(env, [=]() {
return (jlong) builder->build(*engine);
});
}
extern "C" JNIEXPORT jint JNICALL
@@ -94,7 +97,9 @@ Java_com_google_android_filament_IndexBuffer_nSetBuffer(JNIEnv *env, jclass type
BufferDescriptor desc(data, sizeInBytes,
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;
}
@@ -21,6 +21,7 @@
#include <common/NioUtils.h>
#include <common/CallbackUtils.h>
#include <math/mat4.h>
#include <common/JniUtils.h>
using namespace filament;
@@ -37,11 +38,13 @@ Java_com_google_android_filament_IndirectLight_nDestroyBuilder(JNIEnv*, jclass,
}
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) {
IndirectLight::Builder* builder = (IndirectLight::Builder*) nativeBuilder;
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
@@ -17,6 +17,7 @@
#include <jni.h>
#include <filament/LightManager.h>
#include <common/JniUtils.h>
#include <utils/Entity.h>
@@ -24,6 +25,7 @@
using namespace filament;
using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT jint JNICALL
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
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) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
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,
jfloatArray splitPositions, jint cascades) {
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);
}
@@ -231,7 +237,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nComputeLogSplits(JNIEnv* env, jclass,
jfloatArray splitPositions, jint cascades, jfloat near, jfloat far) {
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);
}
@@ -239,7 +247,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nComputePracticalSplits(JNIEnv* env, jclass,
jfloatArray splitPositions, jint cascades, jfloat near, jfloat far, jfloat lambda) {
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);
}
@@ -20,25 +20,28 @@
#include "common/NioUtils.h"
#include "common/CallbackUtils.h"
#include <common/JniUtils.h>
using namespace filament;
using namespace filament::android;
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) {
Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, buffer_, size);
auto builder = Material::Builder();
if (shBandCount) {
builder.sphericalHarmonicsBandCount(shBandCount);
}
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
Material* material = builder
.package(buffer.getData(), buffer.getSize())
.build(*engine);
return (jlong) material;
return wrapJni<jlong>(env, [=]() {
AutoBuffer buffer(env, buffer_, size);
auto builder = Material::Builder();
if (shBandCount) {
builder.sphericalHarmonicsBandCount(shBandCount);
}
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
Material* material = builder
.package(buffer.getData(), buffer.getSize())
.build(*engine);
return (jlong) material;
});
}
extern "C" JNIEXPORT jlong JNICALL
@@ -25,9 +25,11 @@
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <common/JniUtils.h>
using namespace filament;
using namespace filament::math;
using namespace filament::android;
enum BooleanElement {
BOOL,
@@ -56,7 +58,9 @@ template<typename T>
static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
instance->setParameter(name, v);
wrapJni(env, [=]() {
instance->setParameter(name, v);
});
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
// it has the same size as jboolean, which is 1 byte.
switch ((BooleanElement) element) {
case BOOL:
instance->setParameter(name, ((const bool*) v) + offset, count);
break;
case BOOL2:
instance->setParameter(name, ((const bool2*) v) + offset, count);
break;
case BOOL3:
instance->setParameter(name, ((const bool3*) v) + offset, count);
break;
case BOOL4:
instance->setParameter(name, ((const bool4*) v) + offset, count);
break;
}
wrapJni(env, [=]() {
switch ((BooleanElement) element) {
case BOOL:
instance->setParameter(name, ((const bool*) v) + offset, count);
break;
case BOOL2:
instance->setParameter(name, ((const bool2*) v) + offset, count);
break;
case BOOL3:
instance->setParameter(name, ((const bool3*) v) + offset, count);
break;
case BOOL4:
instance->setParameter(name, ((const bool4*) v) + offset, count);
break;
}
});
env->ReleaseBooleanArrayElements(v_, v, 0);
@@ -190,20 +196,22 @@ Java_com_google_android_filament_MaterialInstance_nSetIntParameterArray(JNIEnv *
const char* name = env->GetStringUTFChars(name_, 0);
jint* v = env->GetIntArrayElements(v_, NULL);
switch ((IntElement) element) {
case INT:
instance->setParameter(name, ((const int32_t*) v) + offset, count);
break;
case INT2:
instance->setParameter(name, ((const int2*) v) + offset, count);
break;
case INT3:
instance->setParameter(name, ((const int3*) v) + offset, count);
break;
case INT4:
instance->setParameter(name, ((const int4*) v) + offset, count);
break;
}
wrapJni(env, [=]() {
switch ((IntElement) element) {
case INT:
instance->setParameter(name, ((const int32_t*) v) + offset, count);
break;
case INT2:
instance->setParameter(name, ((const int2*) v) + offset, count);
break;
case INT3:
instance->setParameter(name, ((const int3*) v) + offset, count);
break;
case INT4:
instance->setParameter(name, ((const int4*) v) + offset, count);
break;
}
});
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);
jfloat* v = env->GetFloatArrayElements(v_, NULL);
switch ((FloatElement) element) {
case FLOAT:
instance->setParameter(name, ((const float*) v) + offset, count);
break;
case FLOAT2:
instance->setParameter(name, ((const float2*) v) + offset, count);
break;
case FLOAT3:
instance->setParameter(name, ((const float3*) v) + offset, count);
break;
case FLOAT4:
instance->setParameter(name, ((const float4*) v) + offset, count);
break;
case MAT3:
instance->setParameter(name, ((const mat3f*) v) + offset, count);
break;
case MAT4:
instance->setParameter(name, ((const mat4f*) v) + offset, count);
break;
}
wrapJni(env, [=]() {
switch ((FloatElement) element) {
case FLOAT:
instance->setParameter(name, ((const float*) v) + offset, count);
break;
case FLOAT2:
instance->setParameter(name, ((const float2*) v) + offset, count);
break;
case FLOAT3:
instance->setParameter(name, ((const float3*) v) + offset, count);
break;
case FLOAT4:
instance->setParameter(name, ((const float4*) v) + offset, count);
break;
case MAT3:
instance->setParameter(name, ((const mat3f*) v) + offset, count);
break;
case MAT4:
instance->setParameter(name, ((const mat4f*) v) + offset, count);
break;
}
});
env->ReleaseFloatArrayElements(v_, v, 0);
@@ -260,7 +270,9 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
Texture* texture = (Texture*) nativeTexture;
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);
}
@@ -16,7 +16,6 @@
#include <jni.h>
#include <functional>
#include <stdlib.h>
#include <string.h>
@@ -24,6 +23,7 @@
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament;
using namespace backend;
@@ -81,11 +81,13 @@ extern "C" JNIEXPORT void JNICALL
extern "C"
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) {
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder *) nativeBuilder;
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) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject;
Engine *engine = (Engine *) nativeEngine;
jfloat* data = env->GetFloatArrayElements(positions, NULL);
morphTargetBuffer->setPositionsAt(*engine, targetIndex,
(math::float4*) data, size_t(count));
env->ReleaseFloatArrayElements(positions, data, JNI_ABORT);
return 0;
return filament::android::wrapJni<jint>(env, [=]() {
jfloat* data = env->GetFloatArrayElements(positions, NULL);
morphTargetBuffer->setPositionsAt(*engine, targetIndex,
(math::float4*) data, size_t(count));
env->ReleaseFloatArrayElements(positions, data, JNI_ABORT);
return 0;
});
}
extern "C"
@@ -111,11 +115,13 @@ Java_com_google_android_filament_MorphTargetBuffer_nSetTangentsAt(JNIEnv* env, j
jint targetIndex, jshortArray tangents, jint count) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject;
Engine *engine = (Engine *) nativeEngine;
jshort* data = env->GetShortArrayElements(tangents, NULL);
morphTargetBuffer->setTangentsAt(*engine, targetIndex,
(math::short4*) data, size_t(count));
env->ReleaseShortArrayElements(tangents, data, JNI_ABORT);
return 0;
return filament::android::wrapJni<jint>(env, [=]() {
jshort* data = env->GetShortArrayElements(tangents, NULL);
morphTargetBuffer->setTangentsAt(*engine, targetIndex,
(math::short4*) data, size_t(count));
env->ReleaseShortArrayElements(tangents, data, JNI_ABORT);
return 0;
});
}
extern "C"
@@ -21,6 +21,7 @@
#include <string.h>
#include <filament/RenderTarget.h>
#include <common/JniUtils.h>
using namespace filament;
using namespace backend;
@@ -72,7 +73,9 @@ Java_com_google_android_filament_RenderTarget_nBuilderBuild(JNIEnv *env, jclass
jlong nativeBuilder, jlong nativeEngine) {
RenderTarget::Builder* builder = (RenderTarget::Builder*) nativeBuilder;
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
@@ -18,6 +18,7 @@
#include <filament/RenderableManager.h>
#include <filament/MaterialInstance.h>
#include <common/JniUtils.h>
#include <utils/Entity.h>
@@ -25,6 +26,7 @@
using namespace filament;
using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT jboolean JNICALL
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
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) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
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
@@ -276,11 +280,13 @@ Java_com_google_android_filament_RenderableManager_nBuilderInstances(JNIEnv*, jc
// ------------------------------------------------------------------------------------------------
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) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
SkinningBuffer *sb = (SkinningBuffer *) nativeSkinningBuffer;
rm->setSkinningBuffer(i, sb, count, offset);
wrapJni(env, [=]() {
rm->setSkinningBuffer(i, sb, count, offset);
});
}
extern "C" JNIEXPORT jint JNICALL
@@ -295,9 +301,12 @@ Java_com_google_android_filament_RenderableManager_nSetBonesAsMatrices(JNIEnv* e
// BufferOverflowException
return -1;
}
rm->setBones((RenderableManager::Instance)i,
static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
jint result = 0;
wrapJni(env, [=]() {
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
@@ -312,9 +321,12 @@ Java_com_google_android_filament_RenderableManager_nSetBonesAsQuaternions(JNIEnv
// BufferOverflowException
return -1;
}
rm->setBones((RenderableManager::Instance)i,
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
jint result = 0;
wrapJni(env, [=]() {
rm->setBones((RenderableManager::Instance)i,
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset);
});
return result;
}
extern "C" JNIEXPORT void JNICALL
@@ -323,17 +335,21 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env,
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
jfloat* vec = env->GetFloatArrayElements(weights, NULL);
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);
}
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,
jlong, jint offset) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
rm->setMorphTargetBufferOffsetAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, (size_t) offset);
wrapJni(env, [=]() {
rm->setMorphTargetBufferOffsetAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, (size_t) offset);
});
}
extern "C" JNIEXPORT jint JNICALL
@@ -344,12 +360,14 @@ Java_com_google_android_filament_RenderableManager_nGetMorphTargetCount(JNIEnv*
}
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,
jfloat ex, jfloat ey, jfloat ez) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
rm->setAxisAlignedBoundingBox((RenderableManager::Instance) i, {{cx, cy, cz},
{ex, ey, ez}});
wrapJni(env, [=]() {
rm->setAxisAlignedBoundingBox((RenderableManager::Instance) i, {{cx, cy, cz},
{ex, ey, ez}});
});
}
extern "C" JNIEXPORT void JNICALL
@@ -486,19 +504,23 @@ Java_com_google_android_filament_RenderableManager_nGetInstanceCount(JNIEnv*, jc
}
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) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
const MaterialInstance *materialInstance = (const MaterialInstance *) nativeMaterialInstance;
rm->setMaterialInstanceAt((RenderableManager::Instance) i, (size_t) primitiveIndex,
materialInstance);
wrapJni(env, [=]() {
rm->setMaterialInstanceAt((RenderableManager::Instance) i, (size_t) primitiveIndex,
materialInstance);
});
}
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) {
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
@@ -22,18 +22,24 @@
#include <filament/Viewport.h>
#include <backend/PixelBufferDescriptor.h>
#include <exception>
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include "common/JniUtils.h"
using namespace filament;
using namespace backend;
using namespace filament::android;
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) {
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
wrapJni(env, [=]() {
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
});
}
extern "C" JNIEXPORT jboolean JNICALL
@@ -43,37 +49,45 @@ Java_com_google_android_filament_Renderer_nShouldRenderFrame(JNIEnv *, jclass, j
}
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) {
Renderer *renderer = (Renderer *) nativeRenderer;
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
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->endFrame();
wrapJniBackend(env, [=]() {
renderer->endFrame();
});
}
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) {
Renderer *renderer = (Renderer *) nativeRenderer;
View *view = (View *) nativeView;
renderer->render(view);
wrapJniBackend(env, [=]() {
renderer->render(view);
});
}
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) {
Renderer *renderer = (Renderer *) nativeRenderer;
View *view = (View *) nativeView;
renderer->renderStandaloneView(view);
wrapJni(env, [=]() {
renderer->renderStandaloneView(view);
});
}
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,
jint dstLeft, jint dstBottom, jint dstWidth, jint dstHeight,
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;
const filament::Viewport dstViewport {dstLeft, dstBottom, (uint32_t) dstWidth, (uint32_t) dstHeight};
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
@@ -114,10 +130,11 @@ Java_com_google_android_filament_Renderer_nReadPixels(JNIEnv *env, jclass,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
renderer->readPixels(uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0;
return wrapJni<jint>(env, [&]() {
renderer->readPixels(uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0;
});
}
extern "C" JNIEXPORT jint JNICALL
@@ -150,23 +167,28 @@ Java_com_google_android_filament_Renderer_nReadPixelsEx(JNIEnv *env, jclass,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
renderer->readPixels(renderTarget,
uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0;
return wrapJni<jint>(env, [&]() {
renderer->readPixels(renderTarget,
uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0;
});
}
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;
return renderer->getUserTime();
return wrapJni<jdouble>(env, [=]() {
return renderer->getUserTime();
});
}
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->resetUserTime();
wrapJni(env, [=]() {
renderer->resetUserTime();
});
}
extern "C" JNIEXPORT void JNICALL
@@ -186,20 +208,24 @@ Java_com_google_android_filament_Renderer_nSetFrameRateOptions(JNIEnv*, jclass,
}
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,
jboolean clear, jboolean discard) {
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->setClearOptions({ .clearColor = {r, g, b, a},
.clear = (bool) clear,
.discard = (bool) discard});
wrapJni(env, [=]() {
renderer->setClearOptions({ .clearColor = {r, g, b, a},
.clear = (bool) clear,
.discard = (bool) discard});
});
}
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) {
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->setPresentationTime(monotonicClockNanos);
wrapJni(env, [=]() {
renderer->setPresentationTime(monotonicClockNanos);
});
}
extern "C" JNIEXPORT void JNICALL
@@ -19,9 +19,11 @@
#include <filament/Scene.h>
#include <utils/Entity.h>
#include <common/JniUtils.h>
using namespace filament;
using namespace utils;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL
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,
jint entity) {
Scene* scene = (Scene*) nativeScene;
scene->addEntity((Entity&) entity);
wrapJni(env, [=]() {
scene->addEntity((Entity&) entity);
});
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nAddEntities(JNIEnv *env, jclass type, jlong nativeScene,
jintArray entities) {
Scene* scene = (Scene*) nativeScene;
jsize length = env->GetArrayLength(entities);
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);
}
@@ -59,15 +66,20 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nRemove(JNIEnv *env, jclass type, jlong nativeScene,
jint entity) {
Scene* scene = (Scene*) nativeScene;
scene->remove((Entity&) entity);
wrapJni(env, [=]() {
scene->remove((Entity&) entity);
});
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nRemoveEntities(JNIEnv *env, jclass type, jlong nativeScene,
jintArray entities) {
Scene* scene = (Scene*) nativeScene;
jsize length = env->GetArrayLength(entities);
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);
}
@@ -16,7 +16,6 @@
#include <jni.h>
#include <functional>
#include <stdlib.h>
#include <string.h>
@@ -24,6 +23,7 @@
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament;
using namespace backend;
@@ -60,11 +60,13 @@ Java_com_google_android_filament_SkinningBuffer_nBuilderInitialize(JNIEnv*, jcla
extern "C"
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) {
SkinningBuffer::Builder* builder = (SkinningBuffer::Builder *) nativeBuilder;
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) {
SkinningBuffer *skinningBuffer = (SkinningBuffer *) nativeSkinningBuffer;
Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, matrices, boneCount * 16);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
skinningBuffer->setBones(*engine,
static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
return filament::android::wrapJni<jint>(env, [=]() {
AutoBuffer nioBuffer(env, matrices, boneCount * 16);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
skinningBuffer->setBones(*engine,
static_cast<filament::math::mat4f const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
});
}
extern "C"
@@ -95,16 +99,18 @@ Java_com_google_android_filament_SkinningBuffer_nSetBonesAsQuaternions(JNIEnv* e
jint boneCount, jint offset) {
SkinningBuffer *skinningBuffer = (SkinningBuffer *) nativeSkinningBuffer;
Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, quaternions, boneCount * 8);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
skinningBuffer->setBones(*engine,
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
return filament::android::wrapJni<jint>(env, [=]() {
AutoBuffer nioBuffer(env, quaternions, boneCount * 8);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
skinningBuffer->setBones(*engine,
static_cast<RenderableManager::Bone const *>(data), (size_t)boneCount, (size_t)offset);
return 0;
});
}
extern "C"
@@ -19,6 +19,7 @@
#include <filament/Skybox.h>
#include <math/vec4.h>
#include <common/JniUtils.h>
using namespace filament;
@@ -76,7 +77,9 @@ Java_com_google_android_filament_Skybox_nBuilderBuild(JNIEnv *env, jclass type,
jlong nativeSkyBoxBuilder, jlong nativeEngine) {
Skybox::Builder *builder = (Skybox::Builder *) nativeSkyBoxBuilder;
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
@@ -23,6 +23,7 @@
#include "common/NioUtils.h"
#include "common/CallbackUtils.h"
#include <common/JniUtils.h>
#ifdef __ANDROID__
@@ -113,11 +114,13 @@ Java_com_google_android_filament_Stream_nBuilderHeight(JNIEnv*, jclass,
}
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) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
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
@@ -21,6 +21,7 @@
#include "common/NioUtils.h"
#include <algorithm>
#include <common/JniUtils.h>
using namespace filament;
using namespace filament::geometry;
@@ -125,7 +126,9 @@ extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_SurfaceOrientation_nBuilderBuild(JNIEnv* env, jclass,
jlong 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
@@ -20,6 +20,7 @@
#include <filament/SwapChain.h>
#include "common/CallbackUtils.h"
#include <common/JniUtils.h>
using namespace filament;
@@ -58,11 +59,13 @@ Java_com_google_android_filament_SwapChain_nSetFrameScheduledCallback(JNIEnv* en
jlong nativeSwapChain, jobject handler, jobject runnable) {
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
auto* callback = JniCallback::make(env, handler, runnable);
swapChain->setFrameScheduledCallback(callback->getHandler(),
[callback](backend::PresentCallable) {
// Ignore PresentCallable, which is only meaningful with the Metal backend.
JniCallback::postToJavaAndDestroy(callback);
});
filament::android::wrapJni<void>(env, [=]() {
swapChain->setFrameScheduledCallback(callback->getHandler(),
[callback](backend::PresentCallable) {
// Ignore PresentCallable, which is only meaningful with the Metal backend.
JniCallback::postToJavaAndDestroy(callback);
});
});
}
extern "C" JNIEXPORT jboolean JNICALL
@@ -17,7 +17,6 @@
#include <jni.h>
#include <algorithm>
#include <functional>
#ifdef __ANDROID__
#include <android/bitmap.h>
@@ -38,12 +37,14 @@
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include <common/JniUtils.h>
#include "private/backend/VirtualMachineEnv.h"
using namespace filament;
using namespace backend;
using namespace filament::android;
static size_t getTextureDataSize(const Texture *texture,
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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
(uint32_t) width, (uint32_t) height, (uint32_t) depth,
std::move(desc));
return 0;
return wrapJni<jint>(env, [&]() {
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
(uint32_t) width, (uint32_t) height, (uint32_t) depth,
std::move(desc));
return 0;
});
}
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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
(uint32_t) width, (uint32_t) height, (uint32_t) depth,
std::move(desc));
return 0;
return wrapJni<jint>(env, [&]() {
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
(uint32_t) width, (uint32_t) height, (uint32_t) depth,
std::move(desc));
return 0;
});
}
extern "C" JNIEXPORT jint JNICALL
@@ -346,12 +349,13 @@ Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
return wrapJni<jint>(env, [&]() {
#pragma clang diagnostic push
#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
return 0;
return 0;
});
}
extern "C" JNIEXPORT jint JNICALL
@@ -384,20 +388,23 @@ Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env,
(backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
return wrapJni<jint>(env, [&]() {
#pragma clang diagnostic push
#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
return 0;
return 0;
});
}
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) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
texture->setExternalImage(*engine, (void*)eglImage);
wrapJni(env, [=]() {
texture->setExternalImage(*engine, (void*)eglImage);
});
}
extern "C"
@@ -418,33 +425,35 @@ Java_com_google_android_filament_Texture_nSetExternalImageByAHB(JNIEnv *env, jcl
return JNI_FALSE;
}
if (engine->getBackend() == Backend::OPENGL) {
// CAVEAT: we assume that Backend::OPENGL on Android implies PlatformEGLAndroid.
return wrapJni<jboolean>(env, [=]() {
if (engine->getBackend() == Backend::OPENGL) {
// CAVEAT: we assume that Backend::OPENGL on Android implies PlatformEGLAndroid.
#if UTILS_HAS_RTTI
if (!dynamic_cast<PlatformEGLAndroid*>(platform)) {
return JNI_FALSE;
}
if (!dynamic_cast<PlatformEGLAndroid*>(platform)) {
return JNI_FALSE;
}
#endif
auto* eglPlatform = (PlatformEGLAndroid*) platform;
auto ref = eglPlatform->createExternalImage(nativeBuffer, false);
texture->setExternalImage(*engine, ref);
}
auto* eglPlatform = (PlatformEGLAndroid*) platform;
auto ref = eglPlatform->createExternalImage(nativeBuffer, false);
texture->setExternalImage(*engine, ref);
}
#if FILAMENT_SUPPORTS_VULKAN
else if (engine->getBackend() == Backend::VULKAN) {
// CAVEAT: we assume that Backend::VULKAN on Android implies VulkanPlatformAndroid.
else if (engine->getBackend() == Backend::VULKAN) {
// CAVEAT: we assume that Backend::VULKAN on Android implies VulkanPlatformAndroid.
#if UTILS_HAS_RTTI
if (!dynamic_cast<VulkanPlatformAndroid*>(platform)) {
return JNI_FALSE;
}
if (!dynamic_cast<VulkanPlatformAndroid*>(platform)) {
return JNI_FALSE;
}
#endif
auto* vulkanPlatform = (VulkanPlatformAndroid*) platform;
auto ref = vulkanPlatform->createExternalImage(nativeBuffer, false);
texture->setExternalImage(*engine, ref);
}
auto* vulkanPlatform = (VulkanPlatformAndroid*) platform;
auto ref = vulkanPlatform->createExternalImage(nativeBuffer, false);
texture->setExternalImage(*engine, ref);
}
#endif // FILAMENT_SUPPORTS_VULKAN
// success!
return JNI_TRUE;
// success!
return JNI_TRUE;
});
#else
// other platforms could come here
return JNI_FALSE;
@@ -452,20 +461,24 @@ Java_com_google_android_filament_Texture_nSetExternalImageByAHB(JNIEnv *env, jcl
}
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) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
Stream *stream = (Stream *) nativeStream;
texture->setExternalStream(*engine, stream);
wrapJni(env, [=]() {
texture->setExternalStream(*engine, stream);
});
}
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) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
texture->generateMipmaps(*engine);
wrapJni(env, [=]() {
texture->generateMipmaps(*engine);
});
}
extern "C"
@@ -515,9 +528,10 @@ Java_com_google_android_filament_Texture_nGeneratePrefilterMipmap(JNIEnv *env, j
options.sampleCount = sampleCount;
options.mirror = mirror;
filament::generatePrefilterMipmap(texture, *engine, std::move(desc), faceOffsets, &options);
return 0;
return wrapJni<jint>(env, [&]() {
filament::generatePrefilterMipmap(texture, *engine, std::move(desc), faceOffsets, &options);
return 0;
});
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -640,10 +654,12 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmap(JNIEnv* env, j
autoBitmap->getType(format),
&AutoBitmap::invokeNoCallback, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
filament::android::wrapJni(env, [&]() {
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
});
}
extern "C"
@@ -663,10 +679,12 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmapWithCallback(JN
autoBitmap->getType(format),
autoBitmap->getHandler(), &AutoBitmap::invoke, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
filament::android::wrapJni(env, [&]() {
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
});
}
#endif
@@ -22,9 +22,11 @@
#include <utils/Entity.h>
#include <math/mat4.h>
#include <common/JniUtils.h>
using namespace utils;
using namespace filament;
using namespace filament::android;
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
Java_com_google_android_filament_TransformManager_nCreate(JNIEnv*, jclass,
Java_com_google_android_filament_TransformManager_nCreate(JNIEnv* env, jclass,
jlong nativeTransformManager, jint entity_) {
TransformManager* tm = (TransformManager*) nativeTransformManager;
Entity& entity = *reinterpret_cast<Entity*>(&entity_);
tm->create(entity);
return tm->getInstance(entity);
return wrapJni<jint>(env, [=]() {
Entity entity = *reinterpret_cast<const Entity*>(&entity_);
tm->create(entity);
return tm->getInstance(entity);
});
}
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,
jfloatArray localTransform_) {
TransformManager* tm = (TransformManager*) nativeTransformManager;
Entity& entity = *reinterpret_cast<Entity*>(&entity_);
if (localTransform_) {
jfloat *localTransform = env->GetFloatArrayElements(localTransform_, NULL);
tm->create(entity, (TransformManager::Instance) parent,
*reinterpret_cast<const filament::math::mat4f *>(localTransform));
env->ReleaseFloatArrayElements(localTransform_, localTransform, JNI_ABORT);
} else {
tm->create(entity, (TransformManager::Instance) parent);
}
return tm->getInstance(entity);
return wrapJni<jint>(env, [=]() {
Entity entity = *reinterpret_cast<const Entity*>(&entity_);
if (localTransform_) {
jfloat *localTransform = env->GetFloatArrayElements(localTransform_, NULL);
tm->create(entity, (TransformManager::Instance) parent,
*reinterpret_cast<const filament::math::mat4f *>(localTransform));
env->ReleaseFloatArrayElements(localTransform_, localTransform, JNI_ABORT);
} else {
tm->create(entity, (TransformManager::Instance) parent);
}
return tm->getInstance(entity);
});
}
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,
jdoubleArray localTransform_) {
TransformManager* tm = (TransformManager*) nativeTransformManager;
Entity& entity = *reinterpret_cast<Entity*>(&entity_);
if (localTransform_) {
jdouble *localTransform = env->GetDoubleArrayElements(localTransform_, NULL);
tm->create(entity, (TransformManager::Instance) parent,
*reinterpret_cast<const filament::math::mat4 *>(localTransform));
env->ReleaseDoubleArrayElements(localTransform_, localTransform, JNI_ABORT);
} else {
tm->create(entity, (TransformManager::Instance) parent);
}
return tm->getInstance(entity);
return wrapJni<jint>(env, [=]() {
Entity entity = *reinterpret_cast<const Entity*>(&entity_);
if (localTransform_) {
jdouble *localTransform = env->GetDoubleArrayElements(localTransform_, NULL);
tm->create(entity, (TransformManager::Instance) parent,
*reinterpret_cast<const filament::math::mat4 *>(localTransform));
env->ReleaseDoubleArrayElements(localTransform_, localTransform, JNI_ABORT);
} else {
tm->create(entity, (TransformManager::Instance) parent);
}
return tm->getInstance(entity);
});
}
extern "C" JNIEXPORT void JNICALL
@@ -26,6 +26,7 @@
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include <common/JniUtils.h>
using namespace filament;
using namespace filament::math;
@@ -82,11 +83,13 @@ Java_com_google_android_filament_VertexBuffer_nBuilderNormalized(JNIEnv*, jclass
}
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) {
VertexBuffer::Builder* builder = (VertexBuffer::Builder *) nativeBuilder;
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
@@ -104,30 +107,34 @@ Java_com_google_android_filament_VertexBuffer_nSetBufferAt(JNIEnv *env, jclass,
VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer;
Engine *engine = (Engine *) nativeEngine;
AutoBuffer nioBuffer(env, buffer, count);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
return filament::android::wrapJni<jint>(env, [=]() {
AutoBuffer nioBuffer(env, buffer, count);
void* data = nioBuffer.getData();
size_t sizeInBytes = nioBuffer.getSize();
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// 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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
BufferDescriptor desc(data, sizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
vertexBuffer->setBufferAt(*engine, (uint8_t) bufferIndex, std::move(desc),
(uint32_t) destOffsetInBytes);
vertexBuffer->setBufferAt(*engine, (uint8_t) bufferIndex, std::move(desc),
(uint32_t) destOffsetInBytes);
return 0;
return 0;
});
}
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) {
VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer;
Engine *engine = (Engine *) nativeEngine;
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 "common/CallbackUtils.h"
#include <common/JniUtils.h>
#include "private/backend/VirtualMachineEnv.h"
using namespace filament;
using namespace filament::android;
extern "C" JNIEXPORT void JNICALL
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
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;
Scene* scene = (Scene*) nativeScene;
view->setScene(scene);
wrapJni(env, [=]() {
view->setScene(scene);
});
}
extern "C" JNIEXPORT void JNICALL
@@ -549,21 +553,27 @@ Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass,
extern "C"
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) {
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"
JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nGetMaterialGlobal(JNIEnv *env, jclass clazz,
jlong nativeView, jint index, jfloatArray out_) {
jfloat* out = env->GetFloatArrayElements(out_, nullptr);
View *view = (View *) nativeView;
auto result = view->getMaterialGlobal(index);
std::copy_n(result.v, 4, out);
env->ReleaseFloatArrayElements(out_, out, 0);
wrapJni(env, [=]() {
auto result = view->getMaterialGlobal(index);
jfloat* out = env->GetFloatArrayElements(out_, nullptr);
if (out) {
std::copy_n(result.v, 4, out);
env->ReleaseFloatArrayElements(out_, out, 0);
}
});
}
extern "C"
@@ -91,6 +91,8 @@ public class BufferObject {
* @return the newly created <code>BufferObject</code> object
*
* @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
*/
@@ -321,8 +321,7 @@ public class Engine {
* be initialized, for instance if it doesn't support the right version of OpenGL or
* OpenGL ES.
*
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
* @throws Error if there isn't enough memory to allocate the command buffer.
*/
public Engine build() {
long nativeEngine = nBuilderBuild(mNativeBuilder);
@@ -729,6 +728,8 @@ public class Engine {
*
* @return the active feature level.
*
* @throws RuntimeException if the feature level cannot be set.
*
* @see Builder#featureLevel
* @see #getSupportedFeatureLevel
* @see #getActiveFeatureLevel
@@ -775,6 +776,15 @@ public class Engine {
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}.
*
@@ -1261,9 +1271,10 @@ public class Engine {
* Destroys a {@link Material} and frees all its associated resources.
* <p>
* 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
* @throws RuntimeException if some MaterialInstances remain.
*/
public void destroyMaterial(@NonNull Material material) {
assertDestroy(nDestroyMaterial(getNativeObject(), material.getNativeObject()));
@@ -1580,6 +1591,7 @@ public class Engine {
private static native long nGetEntityManager(long nativeEngine);
private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable);
private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine);
private static native boolean nHasUnrecoverableFailure(long nativeEngine);
private static native long nGetMaxStereoscopicEyes(long nativeEngine);
private static native int nGetSupportedFeatureLevel(long nativeEngine);
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.
*
* @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) {
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) {
int nativeResult = nWaitAndDestroy(fence.getNativeObject(), mode.ordinal());
switch (nativeResult) {
@@ -99,6 +99,7 @@ public class IndexBuffer {
* @return the newly created <code>IndexBuffer</code> object
*
* @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
*/
@@ -316,6 +316,8 @@ public class IndirectLight {
* @return A newly created <code>IndirectLight</code>
*
* @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
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 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) {
if (!nBuilderBuild(mNativeBuilder, engine.getNativeObject(), entity)) {
@@ -476,6 +476,7 @@ public class Material {
* @return the newly created object
*
* @exception IllegalStateException if the material could not be created
* @throws RuntimeException if a parameter to a builder function was invalid.
*/
@NonNull
public Material build(@NonNull Engine engine) {
@@ -156,6 +156,7 @@ public class MaterialInstance {
*
* @param name the name 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) {
nSetParameterBool(getNativeObject(), name, x);
@@ -166,6 +167,7 @@ public class MaterialInstance {
*
* @param name the name 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) {
nSetParameterFloat(getNativeObject(), name, x);
@@ -176,6 +178,7 @@ public class MaterialInstance {
*
* @param name the name 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) {
nSetParameterInt(getNativeObject(), name, x);
@@ -187,6 +190,7 @@ public class MaterialInstance {
* @param name the name of the material parameter
* @param x the value of the first 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) {
nSetParameterBool2(getNativeObject(), name, x, y);
@@ -198,6 +202,7 @@ public class MaterialInstance {
* @param name the name of the material parameter
* @param x the value of the first 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) {
nSetParameterFloat2(getNativeObject(), name, x, y);
@@ -209,6 +214,7 @@ public class MaterialInstance {
* @param name the name of the material parameter
* @param x the value of the first 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) {
nSetParameterInt2(getNativeObject(), name, x, y);
@@ -221,6 +227,7 @@ public class MaterialInstance {
* @param x the value of the first component
* @param y the value of the second 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) {
nSetParameterBool3(getNativeObject(), name, x, y, z);
@@ -233,6 +240,7 @@ public class MaterialInstance {
* @param x the value of the first component
* @param y the value of the second 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) {
nSetParameterFloat3(getNativeObject(), name, x, y, z);
@@ -245,6 +253,7 @@ public class MaterialInstance {
* @param x the value of the first component
* @param y the value of the second 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) {
nSetParameterInt3(getNativeObject(), name, x, y, z);
@@ -258,6 +267,7 @@ public class MaterialInstance {
* @param y the value of the second component
* @param z the value of the third 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) {
nSetParameterBool4(getNativeObject(), name, x, y, z, w);
@@ -271,6 +281,7 @@ public class MaterialInstance {
* @param y the value of the second component
* @param z the value of the third 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) {
nSetParameterFloat4(getNativeObject(), name, x, y, z, w);
@@ -284,6 +295,7 @@ public class MaterialInstance {
* @param y the value of the second component
* @param z the value of the third 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) {
nSetParameterInt4(getNativeObject(), name, x, y, z, w);
@@ -299,6 +311,7 @@ public class MaterialInstance {
* @param name The name of the material texture parameter
* @param texture The texture to set as parameter
* @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,
@NonNull Texture texture, @NonNull TextureSampler sampler) {
@@ -320,6 +333,7 @@ public class MaterialInstance {
* instance.setParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 0, 4);
* }</pre>
* </p>
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/
public void setParameter(@NonNull String name,
@NonNull BooleanElement type, @NonNull boolean[] v,
@@ -342,6 +356,7 @@ public class MaterialInstance {
* instance.setParameter("param", MaterialInstance.IntElement.INT4, a, 0, 4);
* }</pre>
* </p>
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/
public void setParameter(@NonNull String name,
@NonNull IntElement type, @NonNull int[] v,
@@ -364,6 +379,7 @@ public class MaterialInstance {
* material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 0, 4);
* }</pre>
* </p>
* @throws RuntimeException if name doesn't exist or no-op if exceptions are disabled.
*/
public void setParameter(@NonNull String name,
@NonNull FloatElement type, @NonNull float[] v,
@@ -379,6 +395,7 @@ public class MaterialInstance {
* @param r red component
* @param g green 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,
float r, float g, float b) {
@@ -395,6 +412,7 @@ public class MaterialInstance {
* @param g green component
* @param b blue 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,
float r, float g, float b, float a) {
@@ -112,6 +112,8 @@ public class MorphTargetBuffer {
* @return the newly created <code>MorphTargetBuffer</code> object
*
* @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
*/
@@ -587,6 +587,7 @@ public class RenderableManager {
*
* @param engine reference to the <code>Engine</code> to associate this renderable with
* @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) {
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.
* <p>
*
* This must be called between {@link #beginFrame} and {@link #endFrame}.
* </p>
*
* @param monotonicClockNanos The time in nanoseconds corresponding to the system monotonic
* 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)},
* in which case {@link #endFrame} must be called.
*
* @throws Error if the backend thread encountered an unrecoverable error.
*
* @see #endFrame
* @see #render
*/
@@ -370,6 +371,8 @@ public class Renderer {
*
* <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 #render
*/
@@ -425,6 +428,7 @@ public class Renderer {
* </ul>
*
* @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 #endFrame
* @see View
@@ -454,6 +458,7 @@ public class Renderer {
* <li><code>renderStandaloneView()</code> performs potentially heavy computations and cannot be
* multi-threaded. However, internally, it is highly multi-threaded to both improve performance
* and mitigate the call's latency.</li>
* </ul>
*
* @param view the {@link View} to render. This View must have an associated {@link RenderTarget}
* @see View
@@ -519,14 +524,14 @@ public class Renderer {
* <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
* {@link #render}.</p>
* <br>
* <p>After calling this method, the callback associated with <code>buffer</code>
* will be invoked on the main thread, indicating that the read-back has completed.
* Typically, this will happen after multiple calls to {@link #beginFrame},
* {@link #render}, {@link #endFrame}.</p>
* <br>
* <p><code>readPixels</code> is intended for debugging and testing.
* It will impact performance significantly.</p>
*
* <p>After issuing this method, the callback associated with <code>buffer</code> will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen
* after multiple calls to {@link #beginFrame}, {@link #render}, {@link #endFrame}.</p>
*
* <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>
*
* @param xoffset left 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
* {@link #endFrame}.</p>
* <br>
* <p>After calling this method, the callback associated with <code>buffer</code>
* will be invoked on the main thread, indicating that the read-back has completed.
* Typically, this will happen after multiple calls to {@link #beginFrame},
* {@link #render}, {@link #endFrame}.</p>
* <br>
*
* <p>After issuing this method, the callback associated with <code>buffer</code> will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen
* after multiple calls to {@link #beginFrame}, {@link #render}, {@link #endFrame}.</p>
*
* <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
* {@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}
* 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 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
*
* @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 #setBonesAsQuaternions
@@ -885,6 +885,8 @@ public class Texture {
* @return A newly created <code>Texture</code>
* @exception IllegalStateException if a parameter to a builder function was invalid.
* 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
public Texture build(@NonNull Engine engine) {
@@ -272,6 +272,8 @@ public class VertexBuffer {
* @return the newly created <code>VertexBuffer</code> object
*
* @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
public VertexBuffer build(@NonNull Engine engine) {
@@ -23,6 +23,8 @@
#include <utils/Mutex.h>
#include <vector>
#include <exception>
#include <atomic>
#include <stddef.h>
#include <stdint.h>
@@ -75,6 +77,25 @@ public:
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:
const size_t mRequiredSize;
@@ -91,6 +112,12 @@ private:
bool mPaused = false;
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
@@ -75,6 +75,12 @@ public:
// thread via `purge()`.
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;
// The shader languages used for shaders for this driver in order of preference, used to inform
@@ -32,6 +32,7 @@
#include <iterator>
#include <utility>
#include <vector>
#include <exception>
#include <stddef.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() {
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;
if (circularBuffer.empty()) {
+52 -4
View File
@@ -29,6 +29,7 @@
#include "private/backend/Dispatcher.h"
#include "private/backend/Driver.h"
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
@@ -214,18 +215,48 @@ public:
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>
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);
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>
void waitForFence(Predicate predicate) {
FenceStatus waitForFence(Predicate predicate) {
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>
void signalFence(Action action) {
std::lock_guard lock(mFenceMutex);
@@ -233,6 +264,22 @@ public:
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
// --------------------------------------------------------------------------------------------
@@ -275,6 +322,7 @@ private:
std::condition_variable mFenceCondition;
std::mutex mFenceMutex;
std::atomic<bool> mHasUnrecoverableError{false};
};
+1 -1
View File
@@ -78,7 +78,7 @@ private:
ShaderLanguage preferredLanguage) const noexcept final;
// 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.
+1 -1
View File
@@ -307,7 +307,7 @@ void MetalDriver::setFrameCompletedCallback(
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 {
fn();
}
+8 -2
View File
@@ -1428,11 +1428,17 @@ FenceStatus MetalFence::wait(uint64_t timeoutNs) {
}
return false;
};
FenceStatus status;
if (timeoutNs == FENCE_WAIT_FOR_EVER) {
context.driver->waitForFence(predicate);
status = context.driver->waitForFence(predicate);
} else {
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;
}
+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.
// 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.
waitForFence([f] {
FenceStatus status = waitForFence([f] {
return f->fence != nullptr;
}, until);
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
if (f->fence == nullptr) {
// 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;
}
// 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
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
FenceStatus result = FenceStatus::TIMEOUT_EXPIRED;
waitForFence([&] {
FenceStatus status = waitForFence([&] {
if (f->state->status != FenceStatus::TIMEOUT_EXPIRED) {
result = f->state->status;
return true;
}
return false;
}, until);
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
return result;
#else
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;
bool canceled = false;
waitForFence([&] {
auto status = fence->getStatus();
if (bool(status.first) || status.second) {
cmdfence = status.first;
canceled = status.second;
FenceStatus status = waitForFence([&] {
auto fstatus = fence->getStatus();
if (bool(fstatus.first) || fstatus.second) {
cmdfence = fstatus.first;
canceled = fstatus.second;
return true;
}
return false;
}, until);
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
if (!cmdfence || canceled) {
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;
bool const success = waitForFence([&] {
FenceStatus status = waitForFence([&] {
state = fence->getState();
return bool(state);
}, until);
if (!success) {
if (status == FenceStatus::ERROR) {
return FenceStatus::ERROR;
}
if (status == FenceStatus::TIMEOUT_EXPIRED) {
return FenceStatus::TIMEOUT_EXPIRED;
}
+29
View File
@@ -18,6 +18,7 @@
#include "Lifetimes.h"
#include "Skip.h"
#include "../src/DriverBase.h"
using namespace filament;
using namespace filament::backend;
@@ -140,4 +141,32 @@ TEST_F(BackendTest, FrameCompletedCallback) {
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
@@ -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
+2 -2
View File
@@ -273,7 +273,7 @@ public:
* @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.
*/
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.
*
@@ -286,7 +286,7 @@ public:
* @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,
double near, double far) noexcept;
double near, double far);
/** Sets a custom projection matrix for each eye.
*
+18 -1
View File
@@ -782,6 +782,17 @@ public:
*/
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.
*
@@ -1099,6 +1110,8 @@ public:
* in cases where a guarantee about the <code>SwapChain</code> destruction is needed in a
* timely fashion, such as when responding to Android's
* <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();
@@ -1118,6 +1131,8 @@ public:
* @param timeout A timeout in nanoseconds
* @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.
*
* @note If the backend thread has encountered an unrecoverable error, this function becomes a no-op and returns false.
*/
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
* 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();
/**
+3
View File
@@ -62,6 +62,9 @@ public:
* @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::TIMEOUT_EXPIRED if the time out expired or
* 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
*/
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().
*
* @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
*
* @note This method will return false once a backend exception has been delivered to the main thread.
*
* @see
* beginFrame()
*/
@@ -344,6 +346,10 @@ public:
* 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.
*
* @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
* endFrame()
*/
@@ -414,6 +420,9 @@ public:
* @remark
* 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
* beginFrame(), endFrame(), View
*
@@ -506,6 +515,9 @@ public:
* beginFrame() returned true, otherwise, endFrame() must not be called unless the caller
* 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
* beginFrame()
*/
+5 -5
View File
@@ -567,7 +567,7 @@ public:
* @see PlatformCocoaGL::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
@@ -594,7 +594,7 @@ public:
* @deprecated Instead, use setExternalImage(Engine& engine, ExternalImageHandleRef image)
*/
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
@@ -625,7 +625,7 @@ public:
* kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms
* 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
@@ -644,7 +644,7 @@ public:
* @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
@@ -656,7 +656,7 @@ public:
* @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
*/
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
+1 -1
View File
@@ -464,7 +464,7 @@ public:
/**
* 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;
+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);
}
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);
}
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);
}
+4
View File
@@ -474,6 +474,10 @@ bool Engine::isAsynchronousModeEnabled() const noexcept {
return downcast(this)->isAsynchronousModeEnabled();
}
bool Engine::hasUnrecoverableFailure() const noexcept {
return downcast(this)->hasUnrecoverableFailure();
}
size_t Engine::getMaxStereoscopicEyes() noexcept {
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);
}
void Texture::setExternalImage(Engine& engine, ExternalImageHandleRef image) noexcept {
void Texture::setExternalImage(Engine& engine, ExternalImageHandleRef 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);
}
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);
}
void Texture::setExternalStream(Engine& engine, Stream* stream) noexcept {
void Texture::setExternalStream(Engine& engine, Stream* 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));
}
+1 -1
View File
@@ -93,7 +93,7 @@ mat4 FCamera::projection(double const focalLengthInMillimeters,
*/
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)
<< "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).
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,
double const near, double const far) noexcept {
double const near, double const far) {
setCustomProjection(projection, projection, near, far);
}
+81 -7
View File
@@ -91,6 +91,10 @@
#include <unordered_map>
#include <utility>
#ifdef __EXCEPTIONS
#include <exception>
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
@@ -831,6 +835,11 @@ void FEngine::submitFrame() {
}
void FEngine::flush() {
if (UTILS_VERY_UNLIKELY(mCommandBufferQueue.hasUnrecoverableError())) {
return;
}
propagateBackendException();
// flush the command buffer
flushCommandBuffer(mCommandBufferQueue);
@@ -846,6 +855,11 @@ void FEngine::flushAndWait() {
}
bool FEngine::flushAndWait(uint64_t const timeout) {
if (UTILS_VERY_UNLIKELY(mCommandBufferQueue.hasUnrecoverableError())) {
return false;
}
propagateBackendException();
FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isPaused())
<< "Cannot call Engine::flushAndWait() when rendering thread is paused!";
@@ -954,8 +968,10 @@ int FEngine::loop() {
}
void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const {
getDriver().purge();
commandBufferQueue.flush();
if (!commandBufferQueue.hasUnrecoverableError()) {
getDriver().purge();
commandBufferQueue.flush();
}
}
const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
@@ -1594,15 +1610,39 @@ bool FEngine::execute() {
return false;
}
// execute all command buffers
auto& driver = getDriverApi();
for (auto& item : buffers) {
if (UTILS_LIKELY(item.begin)) {
driver.execute(item.begin);
if (UTILS_VERY_UNLIKELY(mCommandBufferQueue.hasUnrecoverableError())) {
for (auto& item : buffers) {
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;
}
@@ -1621,6 +1661,40 @@ void FEngine::setPaused(bool const 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 {
DriverApi& driver = const_cast<FEngine*>(this)->getDriverApi();
return driver.getFeatureLevel();
+30
View File
@@ -83,6 +83,7 @@
#include <string_view>
#include <random>
#include <thread>
#include <mutex>
#include <type_traits>
#include <unordered_map>
@@ -138,6 +139,22 @@ public:
using Epoch = clock::time_point;
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:
static Engine* create(Builder const& builder);
@@ -197,6 +214,15 @@ public:
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 {
return CONFIG_MAX_INSTANCES;
}
@@ -656,6 +682,10 @@ private:
utils::Mutex mFenceListLock;
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
// synchronization objects.
utils::Mutex mSyncListLock;
+7 -32
View File
@@ -37,8 +37,6 @@ namespace filament {
using namespace backend;
utils::Mutex FFence::sLock;
utils::Condition FFence::sCondition;
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
auto& fs = mFenceSignal;
driverApi.queueCommand([fs]() {
fs->signal();
FEngine* const pEngine = &engine;
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();
fs->signal(FenceSignal::DESTROYED);
engine.signalFence(*fs, FenceSignal::DESTROYED);
}
UTILS_NOINLINE
@@ -85,14 +84,14 @@ FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) {
FenceStatus status;
if (UTILS_LIKELY(!engine.pumpPlatformEvents())) {
status = fs->wait(timeout);
status = engine.waitFence(*fs, timeout);
} else {
// 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
// polling and pumping the platform's event queue.
const auto startTime = std::chrono::system_clock::now();
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) {
break;
}
@@ -111,30 +110,6 @@ FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) {
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
+5 -13
View File
@@ -31,6 +31,11 @@ namespace filament {
class FEngine;
struct FenceSignal {
enum State : uint8_t { UNSIGNALED, SIGNALED, DESTROYED };
State mState = UNSIGNALED;
};
class FFence : public Fence {
public:
FFence(FEngine& engine);
@@ -42,19 +47,6 @@ public:
static FenceStatus waitAndDestroy(FFence* fence, Mode mode) noexcept;
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;
// TODO: use custom allocator for these small objects
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);
}
math::mat4f const& FInstanceBuffer::getLocalTransform(size_t index) const noexcept {
math::mat4f const& FInstanceBuffer::getLocalTransform(size_t index) const {
FILAMENT_CHECK_PRECONDITION(index < mInstanceCount)
<< "getLocalTransform overflow: 'index (" << index
<< ") 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);
math::mat4f const& getLocalTransform(size_t index) const noexcept;
math::mat4f const& getLocalTransform(size_t index) const;
void prepare(
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 {
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()) {
mUniforms.clean();
if (isUsingUboBatching()) {
@@ -232,19 +250,6 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
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
fixMissingSamplers();
+14 -1
View File
@@ -316,6 +316,9 @@ void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
}
bool FRenderer::shouldRenderFrame() const noexcept {
if (UTILS_VERY_UNLIKELY(mEngine.hasExceptionBeenRethrown())) {
return false;
}
FEngine& engine = mEngine;
FEngine::DriverApi& driver = engine.getDriverApi();
bool const renderFrame = mFrameSkipper.shouldRenderFrame(driver);
@@ -365,10 +368,16 @@ bool FRenderer::shouldRenderFrame() const noexcept {
}
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));
if (UTILS_VERY_UNLIKELY(mEngine.hasExceptionBeenRethrown())) {
return false;
}
mEngine.propagateBackendException();
if (!vsyncSteadyClockTimeNano) {
vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano;
mVsyncSteadyClockTimeNano = 0;
@@ -469,6 +478,8 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
void FRenderer::endFrame() {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
mEngine.propagateBackendException();
if (UTILS_UNLIKELY(mBeginFrameInternal)) {
mBeginFrameInternal();
mBeginFrameInternal = {};
@@ -637,6 +648,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
void FRenderer::render(FView const* view) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
mEngine.propagateBackendException();
if (UTILS_UNLIKELY(mBeginFrameInternal)) {
// 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.
+7 -7
View File
@@ -656,7 +656,7 @@ AsyncCallId FTexture::setImageAsync(FEngine& engine, size_t const level,
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(isCreationComplete())
<< "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);
}
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(isCreationComplete())
<< "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);
}
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(isCreationComplete())
<< "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);
}
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(isCreationComplete())
<< "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)
<< "External Textures are not mipmappable.";
FILAMENT_CHECK_PRECONDITION(isCreationComplete())
@@ -831,14 +831,14 @@ Handle<HwTexture> FTexture::createPlaceholderTexture(
return handle;
}
backend::Handle<backend::HwTexture> FTexture::getHwHandle() const noexcept {
backend::Handle<backend::HwTexture> FTexture::getHwHandle() const {
FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "Texture is not created yet. It may be in the process of asynchronous loading";
return mHandle;
}
Handle<HwTexture> FTexture::getHwHandleForSampling() const noexcept {
Handle<HwTexture> FTexture::getHwHandleForSampling() const {
FILAMENT_CHECK_PRECONDITION(isCreationComplete())
<< "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
void terminate(FEngine& engine);
backend::Handle<backend::HwTexture> getHwHandle() const noexcept;
backend::Handle<backend::HwTexture> getHwHandleForSampling() const noexcept;
backend::Handle<backend::HwTexture> getHwHandle() const;
backend::Handle<backend::HwTexture> getHwHandleForSampling() const;
size_t getWidth(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,
AsyncCompletionCallback callback, void* user) const;
void setExternalImage(FEngine& engine, ExternalImageHandleRef image) noexcept;
void setExternalImage(FEngine& engine, void* image) noexcept;
void setExternalImage(FEngine& engine, void* image, size_t plane) noexcept;
void setExternalStream(FEngine& engine, FStream* stream) noexcept;
void setExternalImage(FEngine& engine, ExternalImageHandleRef image);
void setExternalImage(FEngine& engine, void* image);
void setExternalImage(FEngine& engine, void* image, size_t plane);
void setExternalStream(FEngine& engine, FStream* stream);
void generateMipmaps(FEngine& engine) const noexcept;
void generateMipmaps(FEngine& engine) const;
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
// fails, it has to assert (or throw), it can't return for e.g. a nullptr, because the public
// 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 {
FILAMENT_CHECK_PRECONDITION(handle) << "Uninitialized handle when using FrameGraphResources.";
+20 -3
View File
@@ -78,6 +78,10 @@
#include <memory>
#include <vector>
#ifdef __EXCEPTIONS
#include <exception>
#endif
#include <stdint.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_Window* sdlWindow = window->getSDLWindow();
#ifdef __EXCEPTIONS
try {
#endif
while (!mClosed) {
if (mWindowTitle != SDL_GetWindowTitle(sdlWindow)) {
SDL_SetWindowTitle(sdlWindow, mWindowTitle.c_str());
@@ -262,7 +269,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
cameraFocalLength = mCameraFocalLength;
cameraNear = mCameraNear;
cameraFar = mCameraFar;
}
}
if (!UTILS_HAS_THREADING) {
mEngine->execute();
@@ -371,10 +378,10 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_RESIZED:
window->resize();
break;
default:
default:
break;
}
break;
@@ -562,6 +569,16 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
++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) {
mImGuiHelper.reset();
+6 -1
View File
@@ -35,6 +35,7 @@
#ifdef __EXCEPTIONS
# define UTILS_EXCEPTIONS 1
# include <exception>
#else
# ifdef UTILS_EXCEPTIONS
# 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
* used for all thrown exceptions.
*/
class UTILS_PUBLIC Panic {
class UTILS_PUBLIC Panic
#ifdef __EXCEPTIONS
: public std::exception
#endif
{
public:
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>
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal,
CString reason) {
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal, CString reason) {
if (reason.empty()) {
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));
// 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();
#endif
// Call the user provided handler
UserPanicHandler::get().call(e);
// if exceptions are enabled, throw now.
#ifdef __EXCEPTIONS
throw e;
throw std::move(e);
#endif
// and finally abort if we somehow get here
+2
View File
@@ -450,6 +450,8 @@ class_<Engine>("Engine")
.function("isAutomaticInstancingEnabled", &Engine::isAutomaticInstancingEnabled)
.function("hasUnrecoverableFailure", &Engine::hasUnrecoverableFailure)
.function("getSupportedFeatureLevel", &Engine::getSupportedFeatureLevel)
.function("setActiveFeatureLevel", &Engine::setActiveFeatureLevel)