Android benchmark frame time (#1958)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
@@ -21,6 +21,12 @@ public:
|
||||
};
|
||||
|
||||
class Renderable {
|
||||
public:
|
||||
enum class SwapBehaviour {
|
||||
NoFlush,
|
||||
Flush
|
||||
};
|
||||
|
||||
protected:
|
||||
Renderable(const Size size_, std::unique_ptr<RenderableResource> resource_)
|
||||
: size(size_),
|
||||
|
||||
@@ -22,6 +22,8 @@ public:
|
||||
backend.setViewport(0, 0, backend.getSize());
|
||||
}
|
||||
|
||||
void swap() override { backend.swap(); }
|
||||
|
||||
private:
|
||||
AndroidRendererBackend& backend;
|
||||
};
|
||||
@@ -62,5 +64,15 @@ void AndroidRendererBackend::markContextLost() {
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidRendererBackend::setSwapBehavior(SwapBehaviour swapBehaviour_) {
|
||||
swapBehaviour = swapBehaviour_;
|
||||
}
|
||||
|
||||
void AndroidRendererBackend::swap() {
|
||||
if (swapBehaviour == SwapBehaviour::Flush) {
|
||||
static_cast<gl::Context&>(getContext()).finish();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -19,6 +19,12 @@ public:
|
||||
void resizeFramebuffer(int width, int height);
|
||||
PremultipliedImage readFramebuffer();
|
||||
|
||||
void setSwapBehavior(SwapBehaviour swapBehaviour);
|
||||
void swap();
|
||||
|
||||
private:
|
||||
SwapBehaviour swapBehaviour = SwapBehaviour::NoFlush;
|
||||
|
||||
// mbgl::gfx::RendererBackend implementation
|
||||
public:
|
||||
mbgl::gfx::Renderable& getDefaultRenderable() override { return *this; }
|
||||
|
||||
@@ -236,6 +236,10 @@ void MapRenderer::onSurfaceDestroyed(JNIEnv&) {
|
||||
resetRenderer();
|
||||
}
|
||||
|
||||
void MapRenderer::setSwapBehaviorFlush(JNIEnv&, jboolean flush) {
|
||||
backend->setSwapBehavior(flush ? gfx::Renderable::SwapBehaviour::Flush : gfx::Renderable::SwapBehaviour::NoFlush);
|
||||
}
|
||||
|
||||
// Static methods //
|
||||
|
||||
void MapRenderer::registerNative(jni::JNIEnv& env) {
|
||||
@@ -256,7 +260,8 @@ void MapRenderer::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&MapRenderer::onRendererReset, "nativeReset"),
|
||||
METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"),
|
||||
METHOD(&MapRenderer::onSurfaceChanged, "nativeOnSurfaceChanged"),
|
||||
METHOD(&MapRenderer::onSurfaceDestroyed, "nativeOnSurfaceDestroyed"));
|
||||
METHOD(&MapRenderer::onSurfaceDestroyed, "nativeOnSurfaceDestroyed"),
|
||||
METHOD(&MapRenderer::setSwapBehaviorFlush, "nativeSetSwapBehaviorFlush"));
|
||||
}
|
||||
|
||||
MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, const jni::Object<MapRenderer>& jObject) {
|
||||
|
||||
@@ -107,6 +107,8 @@ private:
|
||||
// Called on either Main or GL thread //
|
||||
void onRendererReset(JNIEnv&);
|
||||
|
||||
void setSwapBehaviorFlush(JNIEnv&, jboolean flush);
|
||||
|
||||
private:
|
||||
jni::WeakReference<jni::Object<MapRenderer>, jni::EnvAttachingDeleter> javaPeer;
|
||||
|
||||
|
||||
@@ -189,11 +189,15 @@ void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderFrameStatus sta
|
||||
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
||||
static auto onDidFinishRenderingFrame = javaClass.GetMethod<void(jboolean)>(*_env, "onDidFinishRenderingFrame");
|
||||
static auto onDidFinishRenderingFrame = javaClass.GetMethod<void(jboolean, jdouble, jdouble)>(
|
||||
*_env, "onDidFinishRenderingFrame");
|
||||
auto weakReference = javaPeer.get(*_env);
|
||||
if (weakReference) {
|
||||
weakReference.Call(
|
||||
*_env, onDidFinishRenderingFrame, (jboolean)(status.mode != MapObserver::RenderMode::Partial));
|
||||
weakReference.Call(*_env,
|
||||
onDidFinishRenderingFrame,
|
||||
(jboolean)(status.mode != MapObserver::RenderMode::Partial),
|
||||
(jdouble)status.frameEncodingTime,
|
||||
(jdouble)status.frameRenderingTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,11 +135,11 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDidFinishRenderingFrame(boolean fully) {
|
||||
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
try {
|
||||
if (!onDidFinishRenderingFrameList.isEmpty()) {
|
||||
for (MapView.OnDidFinishRenderingFrameListener listener : onDidFinishRenderingFrameList) {
|
||||
listener.onDidFinishRenderingFrame(fully);
|
||||
listener.onDidFinishRenderingFrame(fully, frameEncodingTime, frameRenderingTime);
|
||||
}
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
|
||||
@@ -103,6 +103,10 @@ public final class MapLibreMap {
|
||||
nativeMapView.triggerRepaint();
|
||||
}
|
||||
|
||||
public void setSwapBehaviorFlush(boolean flush) {
|
||||
nativeMapView.setSwapBehaviorFlush(flush);
|
||||
}
|
||||
|
||||
void initialise(@NonNull Context context, @NonNull MapLibreMapOptions options) {
|
||||
transform.initialise(this, options);
|
||||
uiSettings.initialise(context, options);
|
||||
|
||||
@@ -1012,7 +1012,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
*
|
||||
* @param fully true if all frames have been rendered, false if partially rendered
|
||||
*/
|
||||
void onDidFinishRenderingFrame(boolean fully);
|
||||
void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1185,7 +1185,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDidFinishRenderingFrame(boolean fully) {
|
||||
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
if (maplibreMap != null && maplibreMap.getStyle() != null && maplibreMap.getStyle().isFullyLoaded()) {
|
||||
renderCount++;
|
||||
if (renderCount == 3) {
|
||||
@@ -1357,7 +1357,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDidFinishRenderingFrame(boolean fully) {
|
||||
public void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
if (maplibreMap != null) {
|
||||
maplibreMap.onUpdateFullyRendered();
|
||||
}
|
||||
|
||||
@@ -239,6 +239,8 @@ interface NativeMap {
|
||||
|
||||
void triggerRepaint();
|
||||
|
||||
void setSwapBehaviorFlush(boolean flush);
|
||||
|
||||
//
|
||||
// Deprecated Annotations API
|
||||
//
|
||||
|
||||
@@ -1026,6 +1026,11 @@ final class NativeMapView implements NativeMap {
|
||||
nativeTriggerRepaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwapBehaviorFlush(boolean flush) {
|
||||
mapRenderer.setSwapBehaviorFlush(flush);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RectF getDensityDependantRectangle(final RectF rectangle) {
|
||||
@@ -1091,9 +1096,9 @@ final class NativeMapView implements NativeMap {
|
||||
}
|
||||
|
||||
@Keep
|
||||
private void onDidFinishRenderingFrame(boolean fully) {
|
||||
private void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime) {
|
||||
if (stateCallback != null) {
|
||||
stateCallback.onDidFinishRenderingFrame(fully);
|
||||
stateCallback.onDidFinishRenderingFrame(fully, frameEncodingTime, frameRenderingTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1581,7 +1586,7 @@ final class NativeMapView implements NativeMap {
|
||||
|
||||
void onWillStartRenderingFrame();
|
||||
|
||||
void onDidFinishRenderingFrame(boolean fully);
|
||||
void onDidFinishRenderingFrame(boolean fully, double frameEncodingTime, double frameRenderingTime);
|
||||
|
||||
void onWillStartRenderingMap();
|
||||
|
||||
|
||||
@@ -102,6 +102,10 @@ public abstract class MapRenderer implements MapRendererScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
public void setSwapBehaviorFlush(boolean flush) {
|
||||
nativeSetSwapBehaviorFlush(flush);
|
||||
}
|
||||
|
||||
/**
|
||||
* May be called from any thread.
|
||||
* <p>
|
||||
@@ -134,12 +138,16 @@ public abstract class MapRenderer implements MapRendererScheduler {
|
||||
|
||||
private native void nativeRender();
|
||||
|
||||
private native void nativeSetSwapBehaviorFlush(boolean flush);
|
||||
|
||||
private long timeElapsed;
|
||||
|
||||
private void updateFps() {
|
||||
long currentTime = System.nanoTime();
|
||||
double fps = 1E9 / ((currentTime - timeElapsed));
|
||||
onFpsChangedListener.onFpsChanged(fps);
|
||||
if (timeElapsed > 0) {
|
||||
double fps = 1E9 / ((currentTime - timeElapsed));
|
||||
onFpsChangedListener.onFpsChanged(fps);
|
||||
}
|
||||
timeElapsed = currentTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -401,22 +401,22 @@ class MapChangeReceiverTest {
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
mapChangeEventManager!!.removeOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(true,.0, .0)
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
Logger.setLoggerDefinition(loggerDefinition)
|
||||
val exc: Exception = RuntimeException()
|
||||
Mockito.doThrow(exc).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(true)
|
||||
?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: RuntimeException) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -427,9 +427,9 @@ class MapChangeReceiverTest {
|
||||
}
|
||||
val err: Error = ExecutionError("", Error())
|
||||
Mockito.doThrow(err).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(true)
|
||||
?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(true, .0, .0)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: ExecutionError) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -445,22 +445,22 @@ class MapChangeReceiverTest {
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(false)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!.removeOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(false)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
Mockito.verify(onDidFinishRenderingFrameListener)?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
mapChangeEventManager!!.addOnDidFinishRenderingFrameListener(
|
||||
onDidFinishRenderingFrameListener
|
||||
)
|
||||
Logger.setLoggerDefinition(loggerDefinition)
|
||||
val exc: Exception = RuntimeException()
|
||||
Mockito.doThrow(exc).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(false)
|
||||
?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: RuntimeException) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
@@ -471,9 +471,9 @@ class MapChangeReceiverTest {
|
||||
}
|
||||
val err: Error = ExecutionError("", Error())
|
||||
Mockito.doThrow(err).`when`(onDidFinishRenderingFrameListener)
|
||||
?.onDidFinishRenderingFrame(false)
|
||||
?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
try {
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false)
|
||||
mapChangeEventManager!!?.onDidFinishRenderingFrame(false, .0, .0)
|
||||
Assert.fail("The exception should've been re-thrown.")
|
||||
} catch (throwable: ExecutionError) {
|
||||
Mockito.verify(loggerDefinition)?.e(
|
||||
|
||||
@@ -60,7 +60,7 @@ class RemoveUnusedImagesTest : AppCenter() {
|
||||
mapView.addOnCanRemoveUnusedStyleImageListener {
|
||||
callbackLatch.countDown()
|
||||
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 120.0), 8.0))
|
||||
mapView.addOnDidFinishRenderingFrameListener {
|
||||
mapView.addOnDidFinishRenderingFrameListener{ _, _, _ ->
|
||||
assertNotNull(maplibreMap.style!!.getImage("small"))
|
||||
assertNotNull(maplibreMap.style!!.getImage("large"))
|
||||
latch.countDown()
|
||||
@@ -89,7 +89,7 @@ class RemoveUnusedImagesTest : AppCenter() {
|
||||
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 120.0), 8.0))
|
||||
|
||||
// Wait for the next frame and check that images were removed from the style.
|
||||
mapView.addOnDidFinishRenderingFrameListener {
|
||||
mapView.addOnDidFinishRenderingFrameListener { _, _, _ ->
|
||||
if (maplibreMap.style!!.getImage("small") == null && maplibreMap.style!!.getImage("large") == null) {
|
||||
latch.countDown()
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -27,12 +26,14 @@ import org.maplibre.android.BuildConfig.GIT_REVISION
|
||||
import org.maplibre.android.camera.CameraUpdateFactory
|
||||
import org.maplibre.android.geometry.LatLng
|
||||
import org.maplibre.android.log.Logger
|
||||
import org.maplibre.android.log.Logger.INFO
|
||||
import org.maplibre.android.maps.*
|
||||
import org.maplibre.android.maps.MapLibreMap.CancelableCallback
|
||||
import org.maplibre.android.testapp.BuildConfig
|
||||
import org.maplibre.android.testapp.R
|
||||
import org.maplibre.android.testapp.utils.FpsStore
|
||||
import org.maplibre.android.testapp.utils.BenchmarkResults
|
||||
import org.maplibre.android.testapp.utils.FrameTimeStore
|
||||
import java.io.File
|
||||
|
||||
import java.util.*
|
||||
@@ -52,13 +53,32 @@ data class BenchmarkInputData(
|
||||
* Prepares JSON payload that is sent to the API that collects benchmark results.
|
||||
* See https://github.com/maplibre/ci-runners
|
||||
*/
|
||||
fun jsonPayload(results: BenchmarkResults): JsonObject {
|
||||
@SuppressLint("NewApi")
|
||||
fun jsonPayload(styleNames: List<String>, fpsResults: BenchmarkResults, encodingTimeResults: BenchmarkResults, renderingTimeResults: BenchmarkResults): JsonObject {
|
||||
return buildJsonObject {
|
||||
putJsonObject("resultsPerStyle") {
|
||||
for ((styleName, result) in results.resultsPerStyle) {
|
||||
putJsonObject(styleName) {
|
||||
put("avgFps", JsonPrimitive(result.map { it.average }.average()))
|
||||
put("low1p", JsonPrimitive(result.map { it.low1p }.average()))
|
||||
for (style in styleNames) {
|
||||
putJsonObject(style) {
|
||||
fpsResults.resultsPerStyle[style].let { results ->
|
||||
if (results !== null) {
|
||||
put("avgFps", JsonPrimitive(results.map { it.average }.average()))
|
||||
put("low1pFps", JsonPrimitive(results.map { it.low1p }.average()))
|
||||
}
|
||||
}
|
||||
|
||||
encodingTimeResults.resultsPerStyle[style].let { results ->
|
||||
if (results !== null) {
|
||||
put("avgEncodingTime", JsonPrimitive(results.map { it.average }.average()))
|
||||
put("low1pEncodingTime", JsonPrimitive(results.map { it.low1p }.average()))
|
||||
}
|
||||
}
|
||||
|
||||
renderingTimeResults.resultsPerStyle[style].let { results ->
|
||||
if (results !== null) {
|
||||
put("avgRenderingTime", JsonPrimitive(results.map { it.average }.average()))
|
||||
put("low1pRenderingTime", JsonPrimitive(results.map { it.low1p }.average()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,12 +97,16 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
private val TAG = "BenchmarkActivity"
|
||||
|
||||
private lateinit var mapView: MapView
|
||||
private lateinit var maplibreMap: MapLibreMap
|
||||
private var handler: Handler? = null
|
||||
private var delayed: Runnable? = null
|
||||
private var fpsStore = FpsStore()
|
||||
private var results = BenchmarkResults()
|
||||
private var encodingTimeStore = FrameTimeStore()
|
||||
private var renderingTimeStore = FrameTimeStore()
|
||||
private var fpsResults = BenchmarkResults()
|
||||
private var encodingTimeResults = BenchmarkResults()
|
||||
private var renderingTimeResults = BenchmarkResults()
|
||||
private var runsLeft = 5
|
||||
private var measureFrameTime = true
|
||||
|
||||
// the styles used for the benchmark
|
||||
// can be overridden adding with developer-config.xml
|
||||
@@ -160,6 +184,8 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Logger.setVerbosity(INFO)
|
||||
|
||||
setContentView(R.layout.activity_benchmark)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
@@ -179,10 +205,20 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
|
||||
private fun setupMapView(savedInstanceState: Bundle?) {
|
||||
mapView = findViewById<View>(R.id.mapView) as MapView
|
||||
if (measureFrameTime) {
|
||||
mapView.addOnDidFinishRenderingFrameListener { fully: Boolean, frameEncodingTime: Double, frameRenderingTime: Double ->
|
||||
encodingTimeStore.add(frameEncodingTime * 1e3)
|
||||
renderingTimeStore.add(frameRenderingTime * 1e3)
|
||||
}
|
||||
}
|
||||
mapView.getMapAsync { maplibreMap: MapLibreMap ->
|
||||
this@BenchmarkActivity.maplibreMap = maplibreMap
|
||||
maplibreMap.setStyle(inputData.styleURLs[0])
|
||||
setFpsView(maplibreMap)
|
||||
maplibreMap.setSwapBehaviorFlush(measureFrameTime)
|
||||
if (!measureFrameTime) {
|
||||
maplibreMap.setOnFpsChangedListener { fps: Double ->
|
||||
fpsStore.add(fps)
|
||||
}
|
||||
}
|
||||
|
||||
// Start an animation on the map as well
|
||||
flyTo(maplibreMap, 0, 0,14.0)
|
||||
@@ -206,10 +242,29 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
|
||||
override fun onFinish() {
|
||||
if (place == PLACES.size - 1) { // done with tour
|
||||
results.addResult(inputData.styleNames[style], fpsStore)
|
||||
fpsStore.reset()
|
||||
if (measureFrameTime) {
|
||||
encodingTimeResults.addResult(
|
||||
inputData.styleNames[style],
|
||||
encodingTimeStore
|
||||
)
|
||||
encodingTimeStore.reset()
|
||||
|
||||
println("FPS results $results")
|
||||
println("Encoding time results $encodingTimeResults")
|
||||
|
||||
renderingTimeResults.addResult(
|
||||
inputData.styleNames[style],
|
||||
renderingTimeStore
|
||||
)
|
||||
renderingTimeStore.reset()
|
||||
|
||||
println("Rendering time results $renderingTimeResults")
|
||||
} else {
|
||||
fpsResults.addResult(inputData.styleNames[style], fpsStore)
|
||||
fpsStore.reset()
|
||||
|
||||
println("FPS results $fpsResults")
|
||||
}
|
||||
println("Benchmark ${jsonPayload(inputData.styleNames, fpsResults, encodingTimeResults, renderingTimeResults)}")
|
||||
|
||||
if (style < inputData.styleURLs.size - 1) { // continue with next style
|
||||
maplibreMap.setStyle(inputData.styleURLs[style + 1])
|
||||
@@ -231,12 +286,6 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun setFpsView(maplibreMap: MapLibreMap) {
|
||||
maplibreMap.setOnFpsChangedListener { fps: Double ->
|
||||
fpsStore.add(fps)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
mapView.onStart()
|
||||
@@ -284,7 +333,7 @@ class BenchmarkActivity : AppCompatActivity() {
|
||||
|
||||
val client = OkHttpClient()
|
||||
|
||||
val payload = jsonPayload(results)
|
||||
val payload = jsonPayload(inputData.styleNames, fpsResults, encodingTimeResults, renderingTimeResults)
|
||||
Logger.i(TAG, "Sending JSON payload to API: $payload")
|
||||
|
||||
val request = Request.Builder()
|
||||
|
||||
@@ -77,7 +77,7 @@ class MapFragmentActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean) {
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean, frameEncodingTime: Double, frameRenderingTime: Double) {
|
||||
if (initialCameraAnimation && fully && maplibreMap != null) {
|
||||
maplibreMap.animateCamera(
|
||||
CameraUpdateFactory.newCameraPosition(CameraPosition.Builder().tilt(45.0).build()),
|
||||
|
||||
@@ -75,7 +75,7 @@ class SupportMapFragmentActivity :
|
||||
mapView.removeOnDidFinishRenderingFrameListener(this)
|
||||
}
|
||||
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean) {
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean, frameEncodingTime: Double, frameRenderingTime: Double) {
|
||||
if (initialCameraAnimation && fully && maplibreMap != null) {
|
||||
maplibreMap.animateCamera(
|
||||
CameraUpdateFactory.newCameraPosition(CameraPosition.Builder().tilt(45.0).build()),
|
||||
|
||||
@@ -20,7 +20,7 @@ class SnapshotActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
private lateinit var maplibreMap: MapLibreMap
|
||||
|
||||
private val idleListener = object : MapView.OnDidFinishRenderingFrameListener {
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean) {
|
||||
override fun onDidFinishRenderingFrame(fully: Boolean, frameEncodingTime: Double, frameRenderingTime: Double) {
|
||||
if (fully) {
|
||||
binding.mapView.removeOnDidFinishRenderingFrameListener(this)
|
||||
Logger.v(TAG, LOG_MESSAGE)
|
||||
|
||||
@@ -58,7 +58,7 @@ class MapChangeActivity : AppCompatActivity() {
|
||||
mapView.addOnDidFinishLoadingMapListener(OnDidFinishLoadingMapListener { Timber.v("OnDidFinishLoadingMap") })
|
||||
mapView.addOnDidFinishLoadingStyleListener(OnDidFinishLoadingStyleListener { Timber.v("OnDidFinishLoadingStyle") })
|
||||
mapView.addOnDidFinishRenderingFrameListener(
|
||||
OnDidFinishRenderingFrameListener { fully: Boolean ->
|
||||
OnDidFinishRenderingFrameListener { fully: Boolean, frameEncodingTime: Double, frameRenderingTime: Double ->
|
||||
Timber.v(
|
||||
"OnDidFinishRenderingFrame: fully: %s",
|
||||
fully
|
||||
|
||||
@@ -23,6 +23,27 @@ class FpsStore {
|
||||
}
|
||||
}
|
||||
|
||||
class FrameTimeStore {
|
||||
private val timeValues = ArrayList<Double>(100000)
|
||||
|
||||
fun add(time: Double) {
|
||||
timeValues.add(time)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
timeValues.clear()
|
||||
}
|
||||
|
||||
fun low1p(): Double {
|
||||
timeValues.sort()
|
||||
return timeValues.slice((99 * timeValues.size / 100)..timeValues.size - 1).average()
|
||||
}
|
||||
|
||||
fun average(): Double {
|
||||
return timeValues.average()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of single benchmark run
|
||||
*/
|
||||
@@ -41,4 +62,14 @@ data class BenchmarkResults(
|
||||
)
|
||||
resultsPerStyle[styleName] = newResults
|
||||
}
|
||||
|
||||
fun addResult(styleName: String, frameTimeStore: FrameTimeStore) {
|
||||
val newResults = resultsPerStyle.getValue(styleName).plus(
|
||||
BenchmarkResult(
|
||||
frameTimeStore.average(),
|
||||
frameTimeStore.low1p()
|
||||
)
|
||||
)
|
||||
resultsPerStyle[styleName] = newResults
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,6 @@ namespace gfx {
|
||||
// of readStillImage.
|
||||
class HeadlessBackend : public gfx::Renderable {
|
||||
public:
|
||||
enum class SwapBehaviour {
|
||||
NoFlush,
|
||||
Flush
|
||||
};
|
||||
|
||||
// Factory.
|
||||
static std::unique_ptr<HeadlessBackend> Create(const Size size = {256, 256},
|
||||
SwapBehaviour swapBehavior = SwapBehaviour::NoFlush,
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace gfx {
|
||||
|
||||
template <>
|
||||
std::unique_ptr<gfx::HeadlessBackend> Backend::Create<gfx::Backend::Type::OpenGL>(
|
||||
const Size size, gfx::HeadlessBackend::SwapBehaviour swapBehavior, const gfx::ContextMode contextMode) {
|
||||
const Size size, gfx::Renderable::SwapBehaviour swapBehavior, const gfx::ContextMode contextMode) {
|
||||
return std::make_unique<gl::HeadlessBackend>(size, swapBehavior, contextMode);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user