Files
filament/android/filament-android/src/main/cpp/Material.cpp
T
Mathias Agopian f4a079f663 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
2026-04-22 10:56:09 -07:00

318 lines
11 KiB
C++

/*
* Copyright (C) 2017 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 <jni.h>
#include <filament/Material.h>
#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;
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
Java_com_google_android_filament_Material_nGetDefaultInstance(JNIEnv*, jclass,
jlong nativeMaterial) {
Material const* material = (Material const*) nativeMaterial;
return (jlong) material->getDefaultInstance();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Material_nCreateInstance(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jlong) material->createInstance();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Material_nCreateInstanceWithName(JNIEnv* env, jclass,
jlong nativeMaterial, jstring name_) {
Material* material = (Material*) nativeMaterial;
const char *name = env->GetStringUTFChars(name_, 0);
jlong instance = (jlong) material->createInstance(name);
env->ReleaseStringUTFChars(name_, name);
return instance;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_google_android_filament_Material_nGetName(JNIEnv* env, jclass, jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return env->NewStringUTF(material->getName());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetShading(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getShading();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetInterpolation(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getInterpolation();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetBlendingMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getBlendingMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetTransparencyMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getTransparencyMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetRefractionMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint)material->getRefractionMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetRefractionType(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getRefractionType();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetReflectionMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getReflectionMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetFeatureLevel(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getFeatureLevel();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetVertexDomain(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getVertexDomain();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetCullingMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getCullingMode();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nIsColorWriteEnabled(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jboolean) material->isColorWriteEnabled();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nIsDepthWriteEnabled(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jboolean) material->isDepthWriteEnabled();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nIsDepthCullingEnabled(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jboolean) material->isDepthCullingEnabled();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nIsDoubleSided(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jboolean) material->isDoubleSided();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nIsAlphaToCoverageEnabled(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jboolean) material->isAlphaToCoverageEnabled();
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_Material_nGetMaskThreshold(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return material->getMaskThreshold();
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_Material_nGetSpecularAntiAliasingVariance(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return material->getSpecularAntiAliasingVariance();
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_Material_nGetSpecularAntiAliasingThreshold(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return material->getSpecularAntiAliasingThreshold();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetParameterCount(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getParameterCount();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Material_nGetParameters(JNIEnv* env, jclass,
jlong nativeMaterial, jobject parameters, jint count) {
Material* material = (Material*) nativeMaterial;
Material::ParameterInfo* info = new Material::ParameterInfo[count];
size_t received = material->getParameters(info, (size_t) count);
assert(received == count);
jclass parameterClass = env->FindClass("com/google/android/filament/Material$Parameter");
parameterClass = (jclass) env->NewLocalRef(parameterClass);
jmethodID parameterAdd = env->GetStaticMethodID(parameterClass, "add",
"(Ljava/util/List;Ljava/lang/String;III)V");
jfieldID parameterSamplerOffset = env->GetStaticFieldID(parameterClass,
"SAMPLER_OFFSET", "I");
jfieldID parameterSubpassOffset = env->GetStaticFieldID(parameterClass,
"SUBPASS_OFFSET", "I");
jint offset = env->GetStaticIntField(parameterClass, parameterSamplerOffset);
jint subpassOffset = env->GetStaticIntField(parameterClass, parameterSubpassOffset);
for (size_t i = 0; i < received; i++) {
jint type;
if (info[i].isSampler) {
type = (jint) info[i].samplerType + offset;
} else if (info[i].isSubpass) {
type = subpassOffset;
} else {
type = (jint) info[i].type;
}
env->CallStaticVoidMethod(
parameterClass, parameterAdd,
parameters, env->NewStringUTF(info[i].name), type, (jint) info[i].precision,
(jint) info[i].count);
}
env->DeleteLocalRef(parameterClass);
delete[] info;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetRequiredAttributes(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return material->getRequiredAttributes().getValue();
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
jlong nativeMaterial, jstring name_) {
Material* material = (Material*) nativeMaterial;
const char* name = env->GetStringUTFChars(name_, 0);
bool hasParameter = material->hasParameter(name);
env->ReleaseStringUTFChars(name_, name);
return (jboolean) hasParameter;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_android_filament_Material_nGetParameterTransformName(JNIEnv* env, jclass,
jlong nativeMaterial, jstring samplerName_) {
Material* material = (Material*) nativeMaterial;
const char* samplerName = env->GetStringUTFChars(samplerName_, 0);
const char* transformName = material->getParameterTransformName(samplerName);
jstring transformName_ = env->NewStringUTF(transformName ? transformName : "");
env->ReleaseStringUTFChars(samplerName_, samplerName);
return transformName_;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,
jlong nativeMaterial, jint priority, jint variants, jobject handler, jobject runnable) {
Material* material = (Material*) nativeMaterial;
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
material->compile(
(Material::CompilerPriorityQueue) priority,
(UserVariantFilterBit) variants,
jniCallback->getHandler(), [jniCallback](Material*){
JniCallback::postToJavaAndDestroy(jniCallback);
});
}