Skip to content

Commit 5493f97

Browse files
authored
Add experimental function `MapView.setSnapshotLegacyMode (#2423) (#2463)
* Add experimental function * Fixes
1 parent ae0f43b commit 5493f97

File tree

6 files changed

+49
-11
lines changed

6 files changed

+49
-11
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
Mapbox welcomes participation and contributions from everyone.
44

5+
# main
6+
57
# 10.17.1
8+
## Features ✨ and improvements 🏁
9+
* Add experimental `MapView.setSnapshotLegacyMode` function to help avoiding `MapView.snapshot` native crash on some Samsung devices running Android 14.
10+
611
## Bug fixes 🐞
712
* Fix memory leak when camera animations are skipped.
813

@@ -15,7 +20,6 @@ Mapbox welcomes participation and contributions from everyone.
1520
## Dependencies
1621
* Update gl-native to v10.17.0 and common to v23.9.2.
1722

18-
1923
# 10.16.6 March 04, 2024
2024
## Bug fixes 🐞
2125
* Fix rare crash in `LocationServiceImpl` on startup.

sdk/api/PublicRelease/metalava.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ package com.mapbox.maps {
129129
method @com.mapbox.maps.MapboxExperimental public boolean removeWidget(com.mapbox.maps.renderer.widget.Widget widget);
130130
method public void setMaximumFps(@IntRange(from=1, to=Int.MAX_VALUE.toLong()) int fps);
131131
method public void setOnFpsChangedListener(com.mapbox.maps.renderer.OnFpsChangedListener listener);
132+
method @com.mapbox.maps.MapboxExperimental public final void setSnapshotLegacyMode(boolean enabled);
132133
method public android.graphics.Bitmap? snapshot();
133134
method public void snapshot(com.mapbox.maps.MapView.OnSnapshotReady listener);
134135
property public final com.mapbox.maps.viewannotation.ViewAnnotationManager viewAnnotationManager;

sdk/api/sdk.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public class com/mapbox/maps/MapView : android/widget/FrameLayout, com/mapbox/ma
131131
public fun removeWidget (Lcom/mapbox/maps/renderer/widget/Widget;)Z
132132
public fun setMaximumFps (I)V
133133
public fun setOnFpsChangedListener (Lcom/mapbox/maps/renderer/OnFpsChangedListener;)V
134+
public final fun setSnapshotLegacyMode (Z)V
134135
public fun snapshot ()Landroid/graphics/Bitmap;
135136
public fun snapshot (Lcom/mapbox/maps/MapView$OnSnapshotReady;)V
136137
}

sdk/src/main/java/com/mapbox/maps/MapView.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,20 @@ open class MapView : FrameLayout, MapPluginProviderDelegate, MapControllable {
284284
mapController.snapshot(listener)
285285
}
286286

287+
/**
288+
* Set whether legacy mode should be used for [snapshot].
289+
*
290+
* Legacy mode is not that efficient (as it blocks map rendering when making the snapshot)
291+
* but may help with vendor specific issues like described in
292+
* https://github.com/mapbox/mapbox-maps-android/issues/2280.
293+
*
294+
* @param enabled enables legacy mode when True and disables otherwise.
295+
*/
296+
@MapboxExperimental
297+
fun setSnapshotLegacyMode(enabled: Boolean) {
298+
mapController.renderer.snapshotLegacyModeEnabled = enabled
299+
}
300+
287301
/**
288302
* Set new maximum FPS for map rendering.
289303
*

sdk/src/main/java/com/mapbox/maps/renderer/MapboxRenderer.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ internal abstract class MapboxRenderer : MapClient {
4444
}
4545
}
4646

47+
internal var snapshotLegacyModeEnabled = false
48+
@MainThread
49+
get
50+
@MainThread
51+
set
52+
4753
@UiThread
4854
fun onDestroy() {
4955
widgetRenderer.cleanUpAllWidgets()
@@ -144,13 +150,14 @@ internal abstract class MapboxRenderer : MapClient {
144150
}
145151
val lock = ReentrantLock()
146152
val waitCondition = lock.newCondition()
153+
val legacyMode = snapshotLegacyModeEnabled
147154
lock.withLock {
148155
var snapshot: Bitmap? = null
149156
renderThread.queueRenderEvent(
150157
RenderEvent(
151158
runnable = {
152159
lock.withLock {
153-
snapshot = performSnapshot()
160+
snapshot = performSnapshot(legacyMode)
154161
waitCondition.signal()
155162
}
156163
},
@@ -169,9 +176,10 @@ internal abstract class MapboxRenderer : MapClient {
169176
logE(TAG, "Could not take map snapshot because map is not ready yet.")
170177
listener.onSnapshotReady(null)
171178
}
179+
val legacyMode = snapshotLegacyModeEnabled
172180
renderThread.queueRenderEvent(
173181
RenderEvent(
174-
runnable = { listener.onSnapshotReady(performSnapshot()) },
182+
runnable = { listener.onSnapshotReady(performSnapshot(legacyMode)) },
175183
needRender = true,
176184
eventType = EventType.DEFAULT
177185
)
@@ -194,15 +202,15 @@ internal abstract class MapboxRenderer : MapClient {
194202
}
195203

196204
@WorkerThread
197-
private fun performSnapshot(): Bitmap? {
205+
private fun performSnapshot(legacyMode: Boolean): Bitmap? {
198206
if (width == 0 && height == 0) {
199207
logE(TAG, "Could not take map snapshot because map is not ready yet.")
200208
return null
201209
}
202210

203-
if (pixelReader == null || pixelReader?.width != width || pixelReader?.height != height) {
211+
if (pixelReader == null || pixelReader?.width != width || pixelReader?.height != height || pixelReader?.legacyMode != legacyMode) {
204212
pixelReader?.release()
205-
pixelReader = PixelReader(width, height)
213+
pixelReader = PixelReader(width, height, legacyMode)
206214
}
207215
val pixelReader = pixelReader!!
208216

@@ -231,15 +239,15 @@ internal abstract class MapboxRenderer : MapClient {
231239
}
232240
} catch (e: Throwable) {
233241
logW(TAG, "Exception ${e.localizedMessage} happened when reading pixels")
234-
if (pixelReader.supportsPbo) {
242+
if (!pixelReader.legacyMode) {
235243
logW(TAG, "Re-creating PixelReader with no PBO support and making snapshot again")
236244
pixelReader.release()
237245
this.pixelReader = PixelReader(
238246
width = pixelReader.width,
239247
height = pixelReader.height,
240-
supportsPbo = false
248+
legacyMode = true
241249
)
242-
return performSnapshot()
250+
return performSnapshot(legacyMode = true)
243251
}
244252
}
245253
return null

sdk/src/main/java/com/mapbox/maps/renderer/gl/PixelReader.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@ import java.nio.IntBuffer
1717
* 2. By using single PBO with `glReadPixels` on newer devices in order not to block GPU pipeline.
1818
*
1919
* Inspired by http://www.songho.ca/opengl/gl_pbo.html
20+
*
21+
* @param width width in pixels to read
22+
* @param height height in pixels to read
23+
* @param legacyMode if true we read pixels without using PBO (use glReadPixels).
24+
* Otherwise, we use PBO for faster reading if it is supported.
2025
*/
2126
internal class PixelReader(
2227
val width: Int,
2328
val height: Int,
24-
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N)
25-
internal val supportsPbo: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
29+
val legacyMode: Boolean,
2630
) {
31+
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N)
32+
private val supportsPbo: Boolean = if (legacyMode)
33+
false
34+
else
35+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
36+
2737
private val bufferSize = width * height * channelNum
2838
private var buffer = ByteBuffer
2939
.allocateDirect(bufferSize)

0 commit comments

Comments
 (0)