Add asynchronous deletion option for Android renderer backend (#4287)

This commit is contained in:
Adrian Cojocaru
2026-05-13 17:07:13 +03:00
committed by GitHub
parent 6b9b342bf3
commit bccfcddfcf
9 changed files with 68 additions and 8 deletions

View File

@@ -37,13 +37,7 @@ AndroidGLRendererBackend::AndroidGLRendererBackend()
: gl::RendererBackend(gfx::ContextMode::Unique),
mbgl::gfx::Renderable({64, 64}, std::make_unique<AndroidGLRenderableResource>(*this)) {}
AndroidGLRendererBackend::~AndroidGLRendererBackend() {
#ifndef NDEBUG
if (context && static_cast<gl::Context&>(*context).getCleanupOnDestruction()) {
assert(eglGetCurrentContext() != EGL_NO_CONTEXT);
}
#endif
}
AndroidGLRendererBackend::~AndroidGLRendererBackend() = default;
gl::ProcAddress AndroidGLRendererBackend::getExtensionFunctionPointer(const char* name) {
assert(gfx::BackendScope::exists());

View File

@@ -196,7 +196,11 @@ void MapRenderer::requestSnapshot(SnapshotCallback callback) {
void MapRenderer::resetRenderer() {
renderer.reset();
backend.reset();
if (!asyncRendererCleanup) {
backend.reset();
}
window.reset();
swapBehaviorFlush = false;
}

View File

@@ -92,6 +92,8 @@ public:
AndroidRendererBackend& getRendererBackend() const { return *backend; }
const TaggedScheduler& getThreadPool() const { return threadPool; }
void SetAsyncRendererCleanup(bool value) { asyncRendererCleanup = value; }
protected:
// Called from the GL Thread //
@@ -156,6 +158,7 @@ private:
bool framebufferSizeChanged = false;
bool swapBehaviorFlush = false;
bool asyncRendererCleanup = false;
mapbox::base::WeakPtrFactory<Scheduler> weakFactory{this};
// Do not add members here, see `WeakPtrFactory`

View File

@@ -41,5 +41,11 @@ bool NativeMapOptions::crossSourceCollisions(jni::JNIEnv &env, const jni::Object
return obj.Get(env, crossSourceCollisionsField);
}
bool NativeMapOptions::asyncRendererCleanup(jni::JNIEnv &env, const jni::Object<NativeMapOptions> &obj) {
auto &javaClass = jni::Class<NativeMapOptions>::Singleton(env);
auto asyncRendererCleanupField = javaClass.GetField<jni::jboolean>(env, "asyncRendererCleanup");
return obj.Get(env, asyncRendererCleanupField);
}
} // namespace android
} // namespace mbgl

View File

@@ -19,6 +19,7 @@ public:
static util::ActionJournalOptions getActionJournalOptions(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
static float pixelRatio(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
static bool crossSourceCollisions(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
static bool asyncRendererCleanup(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
};
} // namespace android

View File

@@ -74,6 +74,8 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
return;
}
mapRenderer.SetAsyncRendererCleanup(NativeMapOptions::asyncRendererCleanup(_env, jNativeMapOptions));
// Create a renderer frontend
rendererFrontend = AndroidRendererFrontend::create(_env, jMapRenderer);

View File

@@ -100,6 +100,8 @@ public class MapLibreMapOptions implements Parcelable {
private long actionJournalLogFileCount = 5;
private int actionJournalRenderingReportInterval = 60;
private boolean asyncRendererCleanup = false;
/**
* Creates a new MapLibreMapOptions object.
*
@@ -163,6 +165,8 @@ public class MapLibreMapOptions implements Parcelable {
actionJournalLogFileSize = in.readLong();
actionJournalLogFileCount = in.readLong();
actionJournalRenderingReportInterval = in.readInt();
asyncRendererCleanup = in.readByte() != 0;
}
/**
@@ -332,6 +336,10 @@ public class MapLibreMapOptions implements Parcelable {
maplibreMapOptions.actionJournalRenderingReportInterval(
typedArray.getInteger(R.styleable.maplibre_MapView_maplibre_actionJournalRenderingReportInterval, 60)
);
maplibreMapOptions.asyncRendererCleanup(
typedArray.getBoolean(R.styleable.maplibre_MapView_maplibre_asyncRendererCleanup, false)
);
} finally {
typedArray.recycle();
}
@@ -828,6 +836,22 @@ public class MapLibreMapOptions implements Parcelable {
return this;
}
/**
* By default, backend cleanup executes on the render thread
* (with the main thread waiting for the task to finish).
* Enabling this defers cleanup work to the garbage collector/finalzer thread. This can reduce
* shutdown stalls on the main thread, but introduces a non-deterministic cleanup timing and
* might expose possible OpenGL driver issues.
*
* @param asyncRendererCleanup true to enable, false to disable
* @return This
*/
@NonNull
public MapLibreMapOptions asyncRendererCleanup(boolean asyncRendererCleanup) {
this.asyncRendererCleanup = asyncRendererCleanup;
return this;
}
/**
* Enable local ideograph font family, defaults to true.
*
@@ -966,6 +990,15 @@ public class MapLibreMapOptions implements Parcelable {
return actionJournalRenderingReportInterval;
}
/**
* Check whether the renderer backend is async
*
* @return true if async
*/
public boolean getAsyncRendererCleanup() {
return asyncRendererCleanup;
}
/**
* Set the flag to render the map surface on top of another surface.
*
@@ -1356,6 +1389,8 @@ public class MapLibreMapOptions implements Parcelable {
dest.writeLong(actionJournalLogFileSize);
dest.writeLong(actionJournalLogFileCount);
dest.writeInt(actionJournalRenderingReportInterval);
dest.writeByte((byte) (asyncRendererCleanup ? 1 : 0));
}
@Override
@@ -1496,6 +1531,10 @@ public class MapLibreMapOptions implements Parcelable {
return false;
}
if (asyncRendererCleanup != options.asyncRendererCleanup) {
return false;
}
return false;
}
@@ -1548,6 +1587,7 @@ public class MapLibreMapOptions implements Parcelable {
result = 31 * result + (int) actionJournalLogFileSize;
result = 31 * result + (int) actionJournalLogFileCount;
result = 31 * result + (int) actionJournalRenderingReportInterval;
result = 31 * result + (asyncRendererCleanup ? 1 : 0);
return result;
}
}

View File

@@ -10,6 +10,7 @@ public class NativeMapOptions {
private final long actionJournalLogFileSize;
private final long actionJournalLogFileCount;
private final int actionJournalRenderingReportInterval;
private final boolean asyncRendererCleanup;
public NativeMapOptions(MapLibreMapOptions options) {
pixelRatio = options.getPixelRatio();
@@ -20,6 +21,7 @@ public class NativeMapOptions {
actionJournalLogFileSize = options.getActionJournalLogFileSize();
actionJournalLogFileCount = options.getActionJournalLogFileCount();
actionJournalRenderingReportInterval = options.getActionJournalRenderingReportInterval();
asyncRendererCleanup = options.getAsyncRendererCleanup();
}
public NativeMapOptions(float pixelRatio, boolean crossSourceCollisions) {
@@ -31,6 +33,8 @@ public class NativeMapOptions {
actionJournalLogFileSize = 0;
actionJournalLogFileCount = 0;
actionJournalRenderingReportInterval = 0;
asyncRendererCleanup = false;
}
public float pixelRatio() {
@@ -60,4 +64,8 @@ public class NativeMapOptions {
public int actionJournalRenderingReportInterval() {
return actionJournalRenderingReportInterval;
}
public boolean asyncRendererCleanup() {
return asyncRendererCleanup;
}
}

View File

@@ -18,6 +18,8 @@
<attr name="maplibre_actionJournalLogFileCount" format="integer"/>
<attr name="maplibre_actionJournalRenderingReportInterval" format="integer"/>
<attr name="maplibre_asyncRendererCleanup" format="boolean"/>
<!--Camera-->
<attr name="maplibre_cameraTargetLat" format="float"/>
<attr name="maplibre_cameraTargetLng" format="float"/>