Rendering statistics view (#3322)

Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
Adrian Cojocaru
2025-06-03 18:59:51 +03:00
committed by GitHub
parent 3c07cacc4d
commit 2a7249a3b0
81 changed files with 1271 additions and 128 deletions

View File

@@ -23,6 +23,8 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
= new CopyOnWriteArrayList<>();
private final List<MapView.OnDidFinishRenderingFrameListener> onDidFinishRenderingFrameList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnDidFinishRenderingFrameWithStatsListener> onDidFinishRenderingFrameWithStatsList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnWillStartRenderingMapListener> onWillStartRenderingMapListenerList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnDidFinishRenderingMapListener> onDidFinishRenderingMapListenerList
@@ -156,11 +158,17 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
}
@Override
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
public void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
try {
if (!onDidFinishRenderingFrameList.isEmpty()) {
for (MapView.OnDidFinishRenderingFrameListener listener : onDidFinishRenderingFrameList) {
listener.onDidFinishRenderingFrame(fully, frameEncodingTime, frameRenderingTime);
listener.onDidFinishRenderingFrame(fully, stats.encodingTime, stats.renderingTime);
}
}
if (!onDidFinishRenderingFrameWithStatsList.isEmpty()) {
for (MapView.OnDidFinishRenderingFrameWithStatsListener listener : onDidFinishRenderingFrameWithStatsList) {
listener.onDidFinishRenderingFrame(fully, stats);
}
}
} catch (Throwable err) {
@@ -480,6 +488,14 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
onDidFinishRenderingFrameList.remove(listener);
}
void addOnDidFinishRenderingFrameListener(MapView.OnDidFinishRenderingFrameWithStatsListener listener) {
onDidFinishRenderingFrameWithStatsList.add(listener);
}
void removeOnDidFinishRenderingFrameListener(MapView.OnDidFinishRenderingFrameWithStatsListener listener) {
onDidFinishRenderingFrameWithStatsList.remove(listener);
}
void addOnWillStartRenderingMapListener(MapView.OnWillStartRenderingMapListener listener) {
onWillStartRenderingMapListenerList.add(listener);
}

View File

@@ -103,6 +103,20 @@ public final class MapLibreMap {
nativeMapView.triggerRepaint();
}
/**
* Query rendering statistics overlay status.
*/
public boolean isRenderingStatsViewEnabled() {
return nativeMapView.isRenderingStatsViewEnabled();
}
/**
* Enable rendering statistics overlay with {@link RenderingStats} values.
*/
public void enableRenderingStatsView(boolean value) {
nativeMapView.enableRenderingStatsView(value);
}
public void setSwapBehaviorFlush(boolean flush) {
nativeMapView.setSwapBehaviorFlush(flush);
}

View File

@@ -763,6 +763,24 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
mapChangeReceiver.removeOnDidFinishRenderingFrameListener(listener);
}
/**
* Set a callback that's invoked when the map has finished rendering a frame.
*
* @param listener The callback that's invoked when the map has finished rendering a frame
*/
public void addOnDidFinishRenderingFrameListener(@NonNull OnDidFinishRenderingFrameWithStatsListener listener) {
mapChangeReceiver.addOnDidFinishRenderingFrameListener(listener);
}
/**
* Set a callback that's invoked when the map has finished rendering a frame.
*
* @param listener The callback that's invoked when the map has finished rendering a frame
*/
public void removeOnDidFinishRenderingFrameListener(@NonNull OnDidFinishRenderingFrameWithStatsListener listener) {
mapChangeReceiver.removeOnDidFinishRenderingFrameListener(listener);
}
/**
* Set a callback that's invoked when the map will start rendering.
*
@@ -1182,14 +1200,34 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
* </p>
*/
public interface OnDidFinishRenderingFrameListener {
/**
* Called when the map has finished rendering a frame
*
* @param fully true if all frames have been rendered, false if partially rendered
* @param frameEncodingTime CPU encoding time
* @param frameRenderingTime CPU rendering time
*/
void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime);
}
/**
* Interface definition for a callback to be invoked when the map finished rendering a frame.
* <p>
* {@link MapView#addOnDidFinishRenderingFrameListener(OnDidFinishRenderingFrameListener)}
* </p>
*/
public interface OnDidFinishRenderingFrameWithStatsListener {
/**
* Called when the map has finished rendering a frame
*
* @param fully true if all frames have been rendered, false if partially rendered
* @param stats rendering statistics
*/
void onDidFinishRenderingFrame(boolean fully, RenderingStats stats);
}
/**
* Interface definition for a callback to be invoked when the map will start rendering the map.
* <p>
@@ -1528,7 +1566,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
* The initial render callback waits for rendering to happen before making the map visible for end-users.
* We wait for the second DID_FINISH_RENDERING_FRAME map change event as the first will still show a black surface.
*/
private class InitialRenderCallback implements OnDidFinishRenderingFrameListener {
private class InitialRenderCallback implements OnDidFinishRenderingFrameWithStatsListener {
private int renderCount;
@@ -1537,7 +1575,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
}
@Override
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
public void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
if (maplibreMap != null && maplibreMap.getStyle() != null && maplibreMap.getStyle().isFullyLoaded()) {
renderCount++;
if (renderCount == 3) {
@@ -1643,7 +1681,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
}
private class MapCallback implements OnDidFinishLoadingStyleListener,
OnDidFinishRenderingFrameListener, OnDidFinishLoadingMapListener,
OnDidFinishRenderingFrameWithStatsListener, OnDidFinishLoadingMapListener,
OnCameraIsChangingListener, OnCameraDidChangeListener, OnDidFailLoadingMapListener {
private final List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>();
@@ -1709,7 +1747,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
}
@Override
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
public void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
if (maplibreMap != null) {
maplibreMap.onUpdateFullyRendered();
}

View File

@@ -250,6 +250,10 @@ interface NativeMap {
void triggerRepaint();
boolean isRenderingStatsViewEnabled();
void enableRenderingStatsView(boolean value);
void setSwapBehaviorFlush(boolean flush);
//

View File

@@ -1075,6 +1075,16 @@ final class NativeMapView implements NativeMap {
nativeTriggerRepaint();
}
@Override
public boolean isRenderingStatsViewEnabled() {
return nativeIsRenderingStatsViewEnabled();
}
@Override
public void enableRenderingStatsView(boolean value) {
nativeEnableRenderingStatsView(value);
}
@Override
public void setSwapBehaviorFlush(boolean flush) {
mapRenderer.setSwapBehaviorFlush(flush);
@@ -1145,9 +1155,9 @@ final class NativeMapView implements NativeMap {
}
@Keep
private void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
private void onDidFinishRenderingFrame(boolean fully, RenderingStats stats) {
if (stateCallback != null) {
stateCallback.onDidFinishRenderingFrame(fully, frameEncodingTime, frameRenderingTime);
stateCallback.onDidFinishRenderingFrame(fully, stats);
}
}
@@ -1642,6 +1652,12 @@ final class NativeMapView implements NativeMap {
@Keep
private native void nativeTriggerRepaint();
@Keep
private native boolean nativeIsRenderingStatsViewEnabled();
@Keep
private native void nativeEnableRenderingStatsView(boolean enabled);
//
// Snapshot
//
@@ -1719,7 +1735,7 @@ final class NativeMapView implements NativeMap {
void onWillStartRenderingFrame();
void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime);
void onDidFinishRenderingFrame(boolean fully, RenderingStats stats);
void onWillStartRenderingMap();

View File

@@ -0,0 +1,75 @@
package org.maplibre.android.maps;
public class RenderingStats {
/// Frame CPU encoding time (milliseconds)
public double encodingTime = 0.0;
/// Frame CPU rendering time (milliseconds)
public double renderingTime = 0.0;
/// Number of frames rendered
public int numFrames = 0;
/// Number of draw calls (`glDrawElements`, `drawIndexedPrimitives`, etc.) executed during the most recent frame
public int numDrawCalls = 0;
/// Total number of draw calls executed during all the frames
public int totalDrawCalls = 0;
/// Total number of textures created
public int numCreatedTextures = 0;
/// Net textures
public int numActiveTextures = 0;
/// Net texture bindings
public int numTextureBindings = 0;
/// Number of times a texture was updated
public int numTextureUpdates = 0;
/// Number of bytes used in texture updates
public long textureUpdateBytes = 0;
/// Number of buffers created
public long totalBuffers = 0;
/// Number of SDK-specific buffers created
public long totalBufferObjs = 0;
/// Number of times a buffer is updated
public long bufferUpdates = 0;
/// Number of times an SDK-specific buffer is updated
public long bufferObjUpdates = 0;
/// Sum of update sizes
public long bufferUpdateBytes = 0;
/// Number of active buffers
public int numBuffers = 0;
/// Number of active offscreen frame buffers
public int numFrameBuffers = 0;
/// Number of active index buffers
public int numIndexBuffers = 0;
/// Sum of index buffers update sizes
public long indexUpdateBytes = 0;
/// Number of active vertex buffers
public int numVertexBuffers = 0;
/// Sum of vertex buffers update sizes
public long vertexUpdateBytes = 0;
/// Number of active uniform buffers
public int numUniformBuffers = 0;
/// Number of times a uniform buffer is updated
public int numUniformUpdates = 0;
/// Sum of uniform buffers update sizes
public long uniformUpdateBytes = 0;
/// Total texture memory
public int memTextures = 0;
/// Total buffer memory
public int memBuffers = 0;
/// Total index buffer memory
public int memIndexBuffers = 0;
/// Total vertex buffer memory
public int memVertexBuffers = 0;
/// Total uniform buffer memory
public int memUniformBuffers = 0;
/// Number of stencil buffer clears
public int stencilClears = 0;
/// Number of stencil buffer updates
public int stencilUpdates = 0;
}

View File

@@ -2651,6 +2651,26 @@ The unit is in density-independent pixels only for SDF sprites that were created
return new LayoutPropertyValue<>("text-optional", value);
}
/**
* Internal use only
*
* @param value a Boolean value
* @return property wrapper around Boolean
*/
public static PropertyValue<Boolean> symbolScreenSpace(Boolean value) {
return new LayoutPropertyValue<>("symbol-screen-space", value);
}
/**
* Internal use only
*
* @param value a Boolean value
* @return property wrapper around Boolean
*/
public static PropertyValue<Expression> symbolScreenSpace(Expression value) {
return new LayoutPropertyValue<>("symbol-screen-space", value);
}
/**
* Sorts features in ascending order based on this value. Features with a higher sort key will appear above features with a lower sort key.
*

View File

@@ -649,6 +649,18 @@ public class SymbolLayer extends Layer {
return (PropertyValue<Boolean>) new PropertyValue("text-optional", nativeGetTextOptional());
}
/**
* Get the SymbolScreenSpace property
*
* @return property wrapper value around Boolean
*/
@NonNull
@SuppressWarnings("unchecked")
public PropertyValue<Boolean> getSymbolScreenSpace() {
checkThread();
return (PropertyValue<Boolean>) new PropertyValue("symbol-screen-space", nativeGetSymbolScreenSpace());
}
/**
* Get the IconOpacity property
*
@@ -1305,6 +1317,10 @@ public class SymbolLayer extends Layer {
@Keep
private native Object nativeGetTextOptional();
@NonNull
@Keep
private native Object nativeGetSymbolScreenSpace();
@NonNull
@Keep
private native Object nativeGetIconOpacity();