Skip to content

Commit 5775c28

Browse files
jushgithub-actions[bot]
authored andcommitted
[MAPSAND-2981] Make RenderThreadStatsRecorder thread safe and owned by each map (#20562)
GitOrigin-RevId: 4dc3d006ad710bd53edf7c233019c45ef1c54b85
1 parent 76b500b commit 5775c28

13 files changed

Lines changed: 332 additions & 109 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ Mapbox welcomes participation and contributions from everyone.
55
> **16 KB Page Size Support:** Starting with version 11.7.0 and 10.19.0, **NDK 27 is supported** with dedicated artifacts that include [support for 16 KB page sizes](https://developer.android.com/guide/practices/page-sizes). If your app does not require 16 KB page size support, you can keep using our default artifacts without `-ndk27` suffix. For more information about our NDK support, see https://docs.mapbox.com/android/maps/guides/#ndk-support
66

77
# main
8+
## Breaking changes ⚠️
9+
* Remove the deprecated experimental `RenderThreadStats.totalDroppedFrames`. Use `totalSkippedVsync` instead, which holds the same value.
10+
* Replace the experimental `setRenderThreadStatsRecorder()` with a `renderThreadStatsRecorder` property on `MapView` and `MapSurface`. Each map now owns its recorder, and the `RenderThreadStatsRecorder` constructor is no longer public.
11+
* Rename the experimental `RenderThreadStatsRecorder.end()` to `stop()`, to match `start()`.
12+
813
## Features ✨ and improvements 🏁
914
* [compose] Add `showHdRoads` and `colorHdRoads` configuration options to the Standard style.
1015

1116
## Bug fixes 🐞
1217
* Fix a native crash that could occur when the app went to background or the `MapView` was detached while the map was still rendering.
18+
* Fix a race in the experimental `RenderThreadStatsRecorder` where calling `stop()` while the map was rendering could return inconsistent stats or carry frames over into the next recording session. Recording never makes the render thread wait.
1319

1420

1521
## Bug fixes 🐞

‎app/src/main/java/com/mapbox/maps/testapp/examples/SimulateNavigationRouteActivity.kt‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import com.mapbox.maps.MapboxExperimental
1313
import com.mapbox.maps.dsl.cameraOptions
1414
import com.mapbox.maps.logI
1515
import com.mapbox.maps.renderer.RenderThreadStats
16-
import com.mapbox.maps.renderer.RenderThreadStatsRecorder
1716
import com.mapbox.maps.testapp.examples.SimulateNavigationRouteActivity.Companion.SIMULATION_DURATION
1817
import com.mapbox.maps.testapp.examples.annotation.AnnotationUtils
1918
import com.mapbox.maps.testapp.utils.NavigationSimulator
@@ -61,8 +60,7 @@ class SimulateNavigationRouteActivity : AppCompatActivity() {
6160
)
6261
}
6362
repeat(repetitions) {
64-
val renderThreadStatsRecorder = RenderThreadStatsRecorder()
65-
mapView.setRenderThreadStatsRecorder(renderThreadStatsRecorder)
63+
val renderThreadStatsRecorder = mapView.renderThreadStatsRecorder
6664
val navigationSimulator = NavigationSimulator(mapView, routePoints)
6765
navigationSimulator.disableGestures()
6866
renderThreadStatsRecorder.start()
@@ -78,7 +76,7 @@ class SimulateNavigationRouteActivity : AppCompatActivity() {
7876
}
7977
)
8078
delay(SIMULATION_DURATION)
81-
logStats(renderThreadStatsRecorder.end())
79+
logStats(renderThreadStatsRecorder.stop())
8280
navigationSimulator.onDestroy()
8381
}
8482
finish()

‎compose/compose-app/src/main/java/com/mapbox/maps/compose/testapp/utils/FrameStatsRecorder.kt‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@ public class FrameStatsRecorder {
2929
private lateinit var recorder: RenderThreadStatsRecorder
3030
private var writeSummaryJob: Job = Job().apply { cancel() }
3131

32-
public fun register(mapView: MapView) {
33-
recorder = RenderThreadStatsRecorder()
34-
mapView.setRenderThreadStatsRecorder(recorder)
32+
public fun start(mapView: MapView) {
33+
recorder = mapView.renderThreadStatsRecorder
3534
recorder.start()
3635
}
3736

3837
private fun getStatisticalModel(): JsonObject {
39-
val stats = recorder.end()
38+
val stats = recorder.stop()
4039
val jsonObject = JsonObject()
4140
// for TTRC SLAs no recorded render calls happen after "CreateMap" command
4241
// so we return an empty JSON
@@ -110,7 +109,7 @@ public class FrameStatsRecorder {
110109
public fun RecordFrameStats() {
111110
DisposableMapEffect(Unit) {
112111
val frameStatsRecorder = FrameStatsRecorder()
113-
frameStatsRecorder.register(it)
112+
frameStatsRecorder.start(it)
114113
onDispose {
115114
frameStatsRecorder.stop(it)
116115
}

‎maps-sdk/api/Release/metalava.txt‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package com.mapbox.maps {
55
method public void addRendererSetupErrorListener(com.mapbox.maps.renderer.RendererSetupErrorListener rendererSetupErrorListener);
66
method @com.mapbox.maps.MapboxExperimental public void addWidget(com.mapbox.maps.renderer.widget.Widget widget);
77
method public com.mapbox.maps.MapboxMap getMapboxMap();
8+
method public com.mapbox.maps.renderer.RenderThreadStatsRecorder getRenderThreadStatsRecorder();
89
method public boolean onGenericMotionEvent(android.view.MotionEvent event);
910
method public void onSizeChanged(int w, int h);
1011
method public boolean onTouchEvent(android.view.MotionEvent event);
@@ -13,10 +14,10 @@ package com.mapbox.maps {
1314
method @com.mapbox.maps.MapboxExperimental public boolean removeWidget(com.mapbox.maps.renderer.widget.Widget widget);
1415
method @MainThread public void setMaximumFps(int fps);
1516
method public void setOnFpsChangedListener(com.mapbox.maps.renderer.OnFpsChangedListener listener);
16-
method @com.mapbox.maps.MapboxExperimental public void setRenderThreadStatsRecorder(com.mapbox.maps.renderer.RenderThreadStatsRecorder renderThreadStatsRecorder);
1717
method public android.graphics.Bitmap? snapshot();
1818
method public void snapshot(com.mapbox.maps.MapView.OnSnapshotReady listener);
1919
property public abstract com.mapbox.maps.MapboxMap mapboxMap;
20+
property public abstract com.mapbox.maps.renderer.RenderThreadStatsRecorder renderThreadStatsRecorder;
2021
}
2122

2223
public final class MapInitOptions {
@@ -94,6 +95,7 @@ package com.mapbox.maps {
9495
method @Deprecated @kotlin.jvm.JvmSynthetic public com.mapbox.maps.MapboxMap! getMapboxMap();
9596
method @MainThread @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public Integer? getMaximumFps();
9697
method public <T extends com.mapbox.maps.plugin.MapPlugin> T? getPlugin(String id);
98+
method public com.mapbox.maps.renderer.RenderThreadStatsRecorder getRenderThreadStatsRecorder();
9799
method public android.view.Surface getSurface();
98100
method public void onDestroy();
99101
method public boolean onGenericMotionEvent(android.view.MotionEvent event);
@@ -110,7 +112,6 @@ package com.mapbox.maps {
110112
method @com.mapbox.maps.MapboxExperimental public boolean removeWidget(com.mapbox.maps.renderer.widget.Widget widget);
111113
method @MainThread public void setMaximumFps(int fps);
112114
method public void setOnFpsChangedListener(com.mapbox.maps.renderer.OnFpsChangedListener listener);
113-
method @com.mapbox.maps.MapboxExperimental public void setRenderThreadStatsRecorder(com.mapbox.maps.renderer.RenderThreadStatsRecorder renderThreadStatsRecorder);
114115
method public android.graphics.Bitmap? snapshot();
115116
method public void snapshot(com.mapbox.maps.MapView.OnSnapshotReady listener);
116117
method public void surfaceChanged(int width, int height);
@@ -119,6 +120,7 @@ package com.mapbox.maps {
119120
property public final android.content.Context context;
120121
property public com.mapbox.maps.MapboxMap mapboxMap;
121122
property @MainThread @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final Integer? maximumFps;
123+
property public com.mapbox.maps.renderer.RenderThreadStatsRecorder renderThreadStatsRecorder;
122124
property public final android.view.Surface surface;
123125
}
124126

@@ -137,6 +139,7 @@ package com.mapbox.maps {
137139
method @Deprecated @kotlin.jvm.JvmSynthetic public com.mapbox.maps.MapboxMap! getMapboxMap();
138140
method @MainThread @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final Integer? getMaximumFps();
139141
method public <T extends com.mapbox.maps.plugin.MapPlugin> T? getPlugin(String id);
142+
method public com.mapbox.maps.renderer.RenderThreadStatsRecorder getRenderThreadStatsRecorder();
140143
method public final com.mapbox.maps.viewannotation.ViewAnnotationManager getViewAnnotationManager();
141144
method public static final boolean isRenderingSupported();
142145
method public static final boolean isTerrainRenderingSupported();
@@ -156,13 +159,13 @@ package com.mapbox.maps {
156159
method public final void setDebugOptions(java.util.Set<com.mapbox.maps.debugoptions.MapViewDebugOptions>);
157160
method @MainThread public void setMaximumFps(@IntRange(from=1L, to=kotlin.jvm.internal.IntCompanionObject.MAX_VALUE.toLong()) int fps);
158161
method public void setOnFpsChangedListener(com.mapbox.maps.renderer.OnFpsChangedListener listener);
159-
method @com.mapbox.maps.MapboxExperimental public void setRenderThreadStatsRecorder(com.mapbox.maps.renderer.RenderThreadStatsRecorder renderThreadStatsRecorder);
160162
method @com.mapbox.maps.MapboxExperimental public final void setSnapshotLegacyMode(boolean enabled);
161163
method public android.graphics.Bitmap? snapshot();
162164
method public void snapshot(com.mapbox.maps.MapView.OnSnapshotReady listener);
163165
property public final java.util.Set<com.mapbox.maps.debugoptions.MapViewDebugOptions> debugOptions;
164166
property public com.mapbox.maps.MapboxMap mapboxMap;
165167
property @MainThread @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final Integer? maximumFps;
168+
property public com.mapbox.maps.renderer.RenderThreadStatsRecorder renderThreadStatsRecorder;
166169
property public final com.mapbox.maps.viewannotation.ViewAnnotationManager viewAnnotationManager;
167170
field public static final com.mapbox.maps.MapView.Companion Companion;
168171
}
@@ -703,7 +706,6 @@ package com.mapbox.maps.renderer {
703706
method public Double? getPercentile90();
704707
method public Double? getPercentile95();
705708
method public Double? getPercentile99();
706-
method @Deprecated public long getTotalDroppedFrames();
707709
method public long getTotalFrames();
708710
method public long getTotalSkippedVsync();
709711
method public long getTotalTime();
@@ -714,20 +716,21 @@ package com.mapbox.maps.renderer {
714716
property public final Double? percentile90;
715717
property public final Double? percentile95;
716718
property public final Double? percentile99;
717-
property @Deprecated public final long totalDroppedFrames;
718719
property public final long totalFrames;
719720
property public final long totalSkippedVsync;
720721
property public final long totalTime;
721722
}
722723

723724
@com.mapbox.maps.MapboxExperimental public final class RenderThreadStatsRecorder {
724-
ctor public RenderThreadStatsRecorder();
725-
method public com.mapbox.maps.renderer.RenderThreadStats end();
726725
method public boolean isRecording();
727726
method public void start();
727+
method public com.mapbox.maps.renderer.RenderThreadStats stop();
728728
property public final boolean isRecording;
729729
}
730730

731+
public final class RenderThreadStatsRecorderKt {
732+
}
733+
731734
public final class RendererError {
732735
ctor public RendererError(int eglErrorCode);
733736
method public int getEglErrorCode();

‎maps-sdk/api/maps-sdk.api‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public abstract interface class com/mapbox/maps/MapControllable : com/mapbox/map
22
public abstract fun addRendererSetupErrorListener (Lcom/mapbox/maps/renderer/RendererSetupErrorListener;)V
33
public abstract fun addWidget (Lcom/mapbox/maps/renderer/widget/Widget;)V
44
public abstract fun getMapboxMap ()Lcom/mapbox/maps/MapboxMap;
5+
public abstract fun getRenderThreadStatsRecorder ()Lcom/mapbox/maps/renderer/RenderThreadStatsRecorder;
56
public abstract fun onGenericMotionEvent (Landroid/view/MotionEvent;)Z
67
public abstract fun onSizeChanged (II)V
78
public abstract fun onTouchEvent (Landroid/view/MotionEvent;)Z
@@ -10,7 +11,6 @@ public abstract interface class com/mapbox/maps/MapControllable : com/mapbox/map
1011
public abstract fun removeWidget (Lcom/mapbox/maps/renderer/widget/Widget;)Z
1112
public abstract fun setMaximumFps (I)V
1213
public abstract fun setOnFpsChangedListener (Lcom/mapbox/maps/renderer/OnFpsChangedListener;)V
13-
public abstract fun setRenderThreadStatsRecorder (Lcom/mapbox/maps/renderer/RenderThreadStatsRecorder;)V
1414
public abstract fun snapshot ()Landroid/graphics/Bitmap;
1515
public abstract fun snapshot (Lcom/mapbox/maps/MapView$OnSnapshotReady;)V
1616
}
@@ -90,6 +90,7 @@ public final class com/mapbox/maps/MapSurface : com/mapbox/maps/MapControllable,
9090
public final synthetic fun getMapboxMapDeprecated ()Lcom/mapbox/maps/MapboxMap;
9191
public final fun getMaximumFps ()Ljava/lang/Integer;
9292
public fun getPlugin (Ljava/lang/String;)Lcom/mapbox/maps/plugin/MapPlugin;
93+
public fun getRenderThreadStatsRecorder ()Lcom/mapbox/maps/renderer/RenderThreadStatsRecorder;
9394
public final fun getSurface ()Landroid/view/Surface;
9495
public fun onDestroy ()V
9596
public fun onGenericMotionEvent (Landroid/view/MotionEvent;)Z
@@ -106,7 +107,6 @@ public final class com/mapbox/maps/MapSurface : com/mapbox/maps/MapControllable,
106107
public fun removeWidget (Lcom/mapbox/maps/renderer/widget/Widget;)Z
107108
public fun setMaximumFps (I)V
108109
public fun setOnFpsChangedListener (Lcom/mapbox/maps/renderer/OnFpsChangedListener;)V
109-
public fun setRenderThreadStatsRecorder (Lcom/mapbox/maps/renderer/RenderThreadStatsRecorder;)V
110110
public fun snapshot ()Landroid/graphics/Bitmap;
111111
public fun snapshot (Lcom/mapbox/maps/MapView$OnSnapshotReady;)V
112112
public final fun surfaceChanged (II)V
@@ -132,6 +132,7 @@ public class com/mapbox/maps/MapView : android/widget/FrameLayout, com/mapbox/ma
132132
public final synthetic fun getMapboxMapDeprecated ()Lcom/mapbox/maps/MapboxMap;
133133
public final fun getMaximumFps ()Ljava/lang/Integer;
134134
public fun getPlugin (Ljava/lang/String;)Lcom/mapbox/maps/plugin/MapPlugin;
135+
public fun getRenderThreadStatsRecorder ()Lcom/mapbox/maps/renderer/RenderThreadStatsRecorder;
135136
public final fun getViewAnnotationManager ()Lcom/mapbox/maps/viewannotation/ViewAnnotationManager;
136137
public static final fun isRenderingSupported ()Z
137138
public static final fun isTerrainRenderingSupported ()Z
@@ -155,7 +156,6 @@ public class com/mapbox/maps/MapView : android/widget/FrameLayout, com/mapbox/ma
155156
public final fun setDebugOptions (Ljava/util/Set;)V
156157
public fun setMaximumFps (I)V
157158
public fun setOnFpsChangedListener (Lcom/mapbox/maps/renderer/OnFpsChangedListener;)V
158-
public fun setRenderThreadStatsRecorder (Lcom/mapbox/maps/renderer/RenderThreadStatsRecorder;)V
159159
public final fun setSnapshotLegacyMode (Z)V
160160
public fun snapshot ()Landroid/graphics/Bitmap;
161161
public fun snapshot (Lcom/mapbox/maps/MapView$OnSnapshotReady;)V
@@ -751,7 +751,7 @@ public abstract interface annotation class com/mapbox/maps/renderer/RenderThread
751751
}
752752

753753
public final class com/mapbox/maps/renderer/RenderThreadStats {
754-
public synthetic fun <init> (JJJJJJLjava/util/List;Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Double;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
754+
public synthetic fun <init> (JJJJJLjava/util/List;Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Double;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
755755
public fun equals (Ljava/lang/Object;)Z
756756
public final fun getFrameTimeList ()Ljava/util/List;
757757
public final fun getMissedMapRenderFrames ()J
@@ -760,7 +760,6 @@ public final class com/mapbox/maps/renderer/RenderThreadStats {
760760
public final fun getPercentile90 ()Ljava/lang/Double;
761761
public final fun getPercentile95 ()Ljava/lang/Double;
762762
public final fun getPercentile99 ()Ljava/lang/Double;
763-
public final fun getTotalDroppedFrames ()J
764763
public final fun getTotalFrames ()J
765764
public final fun getTotalSkippedVsync ()J
766765
public final fun getTotalTime ()J
@@ -769,10 +768,9 @@ public final class com/mapbox/maps/renderer/RenderThreadStats {
769768
}
770769

771770
public final class com/mapbox/maps/renderer/RenderThreadStatsRecorder {
772-
public fun <init> ()V
773-
public final fun end ()Lcom/mapbox/maps/renderer/RenderThreadStats;
774771
public final fun isRecording ()Z
775772
public final fun start ()V
773+
public final fun stop ()Lcom/mapbox/maps/renderer/RenderThreadStats;
776774
}
777775

778776
public final class com/mapbox/maps/renderer/RendererError {

‎maps-sdk/src/main/java/com/mapbox/maps/MapControllable.kt‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,11 @@ interface MapControllable : MapboxLifecycleObserver {
7878
fun setOnFpsChangedListener(listener: OnFpsChangedListener)
7979

8080
/**
81-
* Set [RenderThreadStatsRecorder] to record frame stats.
81+
* The [RenderThreadStatsRecorder] of this map. Call [RenderThreadStatsRecorder.start] and
82+
* [RenderThreadStatsRecorder.end] on it to record frame stats.
8283
*/
8384
@MapboxExperimental
84-
fun setRenderThreadStatsRecorder(renderThreadStatsRecorder: RenderThreadStatsRecorder)
85+
val renderThreadStatsRecorder: RenderThreadStatsRecorder
8586

8687
/**
8788
* Add [Widget] to the map.

‎maps-sdk/src/main/java/com/mapbox/maps/MapController.kt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,9 @@ internal class MapController : MapPluginProviderDelegate, MapControllable {
324324
renderer.setOnFpsChangedListener(listener)
325325
}
326326

327-
override fun setRenderThreadStatsRecorder(renderThreadStatsRecorder: RenderThreadStatsRecorder) {
328-
renderer.renderThread.renderThreadStatsRecorder = renderThreadStatsRecorder
329-
}
327+
@MapboxExperimental
328+
override val renderThreadStatsRecorder: RenderThreadStatsRecorder
329+
get() = renderer.renderThread.renderThreadStatsRecorder
330330

331331
@OptIn(MapboxExperimental::class)
332332
override fun addWidget(widget: Widget) {

‎maps-sdk/src/main/java/com/mapbox/maps/MapSurface.kt‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,12 @@ class MapSurface : MapPluginProviderDelegate, MapControllable {
283283
}
284284

285285
/**
286-
* Set [RenderThreadStatsRecorder] to record frame stats.
286+
* The [RenderThreadStatsRecorder] of this map. Call [RenderThreadStatsRecorder.start] and
287+
* [RenderThreadStatsRecorder.end] on it to record frame stats.
287288
*/
288289
@MapboxExperimental
289-
override fun setRenderThreadStatsRecorder(renderThreadStatsRecorder: RenderThreadStatsRecorder) {
290-
mapController.setRenderThreadStatsRecorder(renderThreadStatsRecorder)
291-
}
290+
override val renderThreadStatsRecorder: RenderThreadStatsRecorder
291+
get() = mapController.renderThreadStatsRecorder
292292

293293
/**
294294
* Called to resume rendering

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -588,12 +588,12 @@ open class MapView : FrameLayout, MapPluginProviderDelegate, MapControllable {
588588
}
589589

590590
/**
591-
* Set [RenderThreadStatsRecorder] to record frame stats.
591+
* The [RenderThreadStatsRecorder] of this map. Call [RenderThreadStatsRecorder.start] and
592+
* [RenderThreadStatsRecorder.end] on it to record frame stats.
592593
*/
593594
@MapboxExperimental
594-
override fun setRenderThreadStatsRecorder(renderThreadStatsRecorder: RenderThreadStatsRecorder) {
595-
mapController.setRenderThreadStatsRecorder(renderThreadStatsRecorder)
596-
}
595+
override val renderThreadStatsRecorder: RenderThreadStatsRecorder
596+
get() = mapController.renderThreadStatsRecorder
597597

598598
/**
599599
* Add [Widget] to the map.

0 commit comments

Comments
 (0)