Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions android/common/CallbackUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*,
Expand Down
3 changes: 3 additions & 0 deletions android/common/CallbackUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
51 changes: 51 additions & 0 deletions android/common/JniUtils.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 123 additions & 0 deletions android/common/JniUtils.h
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions android/filament-android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 18 additions & 14 deletions android/filament-android/src/main/cpp/BufferObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#include <jni.h>

#include <functional>
#include <stdlib.h>
#include <string.h>

Expand All @@ -26,6 +25,7 @@

#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include <common/JniUtils.h>

using namespace filament;
using namespace backend;
Expand Down Expand Up @@ -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
Expand All @@ -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;
});
}
44 changes: 29 additions & 15 deletions android/filament-android/src/main/cpp/Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,34 @@
#include <jni.h>

#include <filament/Camera.h>
#include <common/JniUtils.h>


#include <utils/Entity.h>

#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
Expand All @@ -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
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}

Expand Down
7 changes: 5 additions & 2 deletions android/filament-android/src/main/cpp/ColorGrading.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include <math/vec3.h>
#include <math/vec4.h>
#include <common/JniUtils.h>

using namespace filament;
using namespace math;
Expand All @@ -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
Expand Down
Loading
Loading