Benchmark app frame time improvements (#1776)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -42,7 +42,8 @@ public:
|
|||||||
RenderMode mode;
|
RenderMode mode;
|
||||||
bool needsRepaint; // In continous mode, shows that there are ongoig transitions.
|
bool needsRepaint; // In continous mode, shows that there are ongoig transitions.
|
||||||
bool placementChanged;
|
bool placementChanged;
|
||||||
double frameTime;
|
double frameEncodingTime;
|
||||||
|
double frameRenderingTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void onCameraWillChange(CameraChangeMode) {}
|
virtual void onCameraWillChange(CameraChangeMode) {}
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ public:
|
|||||||
virtual void onDidFinishRenderingFrame(RenderMode, bool /*repaint*/, bool /*placementChanged*/) {}
|
virtual void onDidFinishRenderingFrame(RenderMode, bool /*repaint*/, bool /*placementChanged*/) {}
|
||||||
|
|
||||||
/// End of frame, booleans flags that a repaint is required and that placement changed.
|
/// End of frame, booleans flags that a repaint is required and that placement changed.
|
||||||
virtual void onDidFinishRenderingFrame(RenderMode mode, bool repaint, bool placementChanged, double /*frameTime*/) {
|
virtual void onDidFinishRenderingFrame(RenderMode mode,
|
||||||
|
bool repaint,
|
||||||
|
bool placementChanged,
|
||||||
|
double /*frameEncodingTime*/,
|
||||||
|
double /*frameRenderingTime*/) {
|
||||||
onDidFinishRenderingFrame(mode, repaint, placementChanged);
|
onDidFinishRenderingFrame(mode, repaint, placementChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ objc_library(
|
|||||||
|
|
||||||
objc_library(
|
objc_library(
|
||||||
name = "ios-benchapp",
|
name = "ios-benchapp",
|
||||||
copts = MAPLIBRE_FLAGS,
|
copts = CPP_FLAGS + MAPLIBRE_FLAGS,
|
||||||
srcs = ["//platform/ios/benchmark:ios_benchmark_srcs"],
|
srcs = ["//platform/ios/benchmark:ios_benchmark_srcs"],
|
||||||
hdrs = ["//platform/ios/benchmark:ios_benchmark_hdrs"],
|
hdrs = ["//platform/ios/benchmark:ios_benchmark_hdrs"],
|
||||||
includes = [
|
includes = [
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ public:
|
|||||||
void onDidFinishRenderingFrame(RenderMode mode,
|
void onDidFinishRenderingFrame(RenderMode mode,
|
||||||
bool repaintNeeded,
|
bool repaintNeeded,
|
||||||
bool placementChanged,
|
bool placementChanged,
|
||||||
double frameTime) override {
|
double frameEncodingTime,
|
||||||
void (RendererObserver::*f)(RenderMode, bool, bool, double) = &RendererObserver::onDidFinishRenderingFrame;
|
double frameRenderingTime) override {
|
||||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameTime);
|
void (RendererObserver::*f)(
|
||||||
|
RenderMode, bool, bool, double, double) = &RendererObserver::onDidFinishRenderingFrame;
|
||||||
|
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDidFinishRenderingMap() override { delegate.invoke(&RendererObserver::onDidFinishRenderingMap); }
|
void onDidFinishRenderingMap() override { delegate.invoke(&RendererObserver::onDidFinishRenderingMap); }
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ public:
|
|||||||
float pixelRatio_,
|
float pixelRatio_,
|
||||||
gfx::HeadlessBackend::SwapBehaviour swapBehavior = gfx::HeadlessBackend::SwapBehaviour::NoFlush,
|
gfx::HeadlessBackend::SwapBehaviour swapBehavior = gfx::HeadlessBackend::SwapBehaviour::NoFlush,
|
||||||
gfx::ContextMode mode = gfx::ContextMode::Unique,
|
gfx::ContextMode mode = gfx::ContextMode::Unique,
|
||||||
const std::optional<std::string>& localFontFamily = std::nullopt);
|
const std::optional<std::string>& localFontFamily = std::nullopt,
|
||||||
|
bool invalidateOnUpdate_ = true);
|
||||||
~HeadlessFrontend() override;
|
~HeadlessFrontend() override;
|
||||||
|
|
||||||
void reset() override;
|
void reset() override;
|
||||||
@@ -56,6 +57,7 @@ public:
|
|||||||
PremultipliedImage readStillImage();
|
PremultipliedImage readStillImage();
|
||||||
RenderResult render(Map&);
|
RenderResult render(Map&);
|
||||||
void renderOnce(Map&);
|
void renderOnce(Map&);
|
||||||
|
void renderFrame();
|
||||||
|
|
||||||
std::optional<TransformState> getTransformState() const;
|
std::optional<TransformState> getTransformState() const;
|
||||||
|
|
||||||
@@ -66,6 +68,7 @@ private:
|
|||||||
std::atomic<double> frameTime;
|
std::atomic<double> frameTime;
|
||||||
std::unique_ptr<gfx::HeadlessBackend> backend;
|
std::unique_ptr<gfx::HeadlessBackend> backend;
|
||||||
util::AsyncTask asyncInvalidate;
|
util::AsyncTask asyncInvalidate;
|
||||||
|
bool invalidateOnUpdate;
|
||||||
|
|
||||||
std::unique_ptr<Renderer> renderer;
|
std::unique_ptr<Renderer> renderer;
|
||||||
std::shared_ptr<UpdateParameters> updateParameters;
|
std::shared_ptr<UpdateParameters> updateParameters;
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ HeadlessFrontend::HeadlessFrontend(Size size_,
|
|||||||
float pixelRatio_,
|
float pixelRatio_,
|
||||||
gfx::HeadlessBackend::SwapBehaviour swapBehavior,
|
gfx::HeadlessBackend::SwapBehaviour swapBehavior,
|
||||||
const gfx::ContextMode contextMode,
|
const gfx::ContextMode contextMode,
|
||||||
const std::optional<std::string>& localFontFamily)
|
const std::optional<std::string>& localFontFamily,
|
||||||
|
bool invalidateOnUpdate_)
|
||||||
: size(size_),
|
: size(size_),
|
||||||
pixelRatio(pixelRatio_),
|
pixelRatio(pixelRatio_),
|
||||||
frameTime(0),
|
frameTime(0),
|
||||||
@@ -29,23 +30,8 @@ HeadlessFrontend::HeadlessFrontend(Size size_,
|
|||||||
{static_cast<uint32_t>(size.width * pixelRatio), static_cast<uint32_t>(size.height * pixelRatio)},
|
{static_cast<uint32_t>(size.width * pixelRatio), static_cast<uint32_t>(size.height * pixelRatio)},
|
||||||
swapBehavior,
|
swapBehavior,
|
||||||
contextMode)),
|
contextMode)),
|
||||||
asyncInvalidate([this] {
|
asyncInvalidate([this] { renderFrame(); }),
|
||||||
if (renderer && updateParameters) {
|
invalidateOnUpdate(invalidateOnUpdate_),
|
||||||
auto startTime = mbgl::util::MonotonicTimer::now();
|
|
||||||
gfx::BackendScope guard{*getBackend()};
|
|
||||||
|
|
||||||
// onStyleImageMissing might be called during a render. The user
|
|
||||||
// implemented method could trigger a call to
|
|
||||||
// MLNRenderFrontend#update which overwrites `updateParameters`.
|
|
||||||
// Copy the shared pointer here so that the parameters aren't
|
|
||||||
// destroyed while `render(...)` is still using them.
|
|
||||||
auto updateParameters_ = updateParameters;
|
|
||||||
renderer->render(updateParameters_);
|
|
||||||
|
|
||||||
auto endTime = mbgl::util::MonotonicTimer::now();
|
|
||||||
frameTime = (endTime - startTime).count();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
renderer(std::make_unique<Renderer>(*getBackend(), pixelRatio, localFontFamily)) {}
|
renderer(std::make_unique<Renderer>(*getBackend(), pixelRatio, localFontFamily)) {}
|
||||||
|
|
||||||
HeadlessFrontend::~HeadlessFrontend() = default;
|
HeadlessFrontend::~HeadlessFrontend() = default;
|
||||||
@@ -57,7 +43,9 @@ void HeadlessFrontend::reset() {
|
|||||||
|
|
||||||
void HeadlessFrontend::update(std::shared_ptr<UpdateParameters> updateParameters_) {
|
void HeadlessFrontend::update(std::shared_ptr<UpdateParameters> updateParameters_) {
|
||||||
updateParameters = updateParameters_;
|
updateParameters = updateParameters_;
|
||||||
asyncInvalidate.send();
|
if (invalidateOnUpdate) {
|
||||||
|
asyncInvalidate.send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeadlessFrontend::setObserver(RendererObserver& observer_) {
|
void HeadlessFrontend::setObserver(RendererObserver& observer_) {
|
||||||
@@ -169,6 +157,24 @@ void HeadlessFrontend::renderOnce(Map&) {
|
|||||||
util::RunLoop::Get()->runOnce();
|
util::RunLoop::Get()->runOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeadlessFrontend::renderFrame() {
|
||||||
|
if (renderer && updateParameters) {
|
||||||
|
auto startTime = mbgl::util::MonotonicTimer::now();
|
||||||
|
gfx::BackendScope guard{*getBackend()};
|
||||||
|
|
||||||
|
// onStyleImageMissing might be called during a render. The user
|
||||||
|
// implemented method could trigger a call to
|
||||||
|
// MLNRenderFrontend#update which overwrites `updateParameters`.
|
||||||
|
// Copy the shared pointer here so that the parameters aren't
|
||||||
|
// destroyed while `render(...)` is still using them.
|
||||||
|
auto updateParameters_ = updateParameters;
|
||||||
|
renderer->render(updateParameters_);
|
||||||
|
|
||||||
|
auto endTime = mbgl::util::MonotonicTimer::now();
|
||||||
|
frameTime = (endTime - startTime).count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<TransformState> HeadlessFrontend::getTransformState() const {
|
std::optional<TransformState> HeadlessFrontend::getTransformState() const {
|
||||||
if (updateParameters) {
|
if (updateParameters) {
|
||||||
return updateParameters->transformState;
|
return updateParameters->transformState;
|
||||||
|
|||||||
@@ -39,9 +39,11 @@ public:
|
|||||||
void onDidFinishRenderingFrame(RenderMode mode,
|
void onDidFinishRenderingFrame(RenderMode mode,
|
||||||
bool repaintNeeded,
|
bool repaintNeeded,
|
||||||
bool placementChanged,
|
bool placementChanged,
|
||||||
double frameTime) override {
|
double frameEncodingTime,
|
||||||
void (RendererObserver::*f)(RenderMode, bool, bool, double) = &RendererObserver::onDidFinishRenderingFrame;
|
double frameRenderingTime) override {
|
||||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameTime);
|
void (RendererObserver::*f)(
|
||||||
|
RenderMode, bool, bool, double, double) = &RendererObserver::onDidFinishRenderingFrame;
|
||||||
|
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStyleImageMissing(const std::string& image, const StyleImageMissingCallback& cb) override {
|
void onStyleImageMissing(const std::string& image, const StyleImageMissingCallback& cb) override {
|
||||||
@@ -84,11 +86,13 @@ public:
|
|||||||
void onDidFinishRenderingFrame(RenderMode mode,
|
void onDidFinishRenderingFrame(RenderMode mode,
|
||||||
bool repaintNeeded,
|
bool repaintNeeded,
|
||||||
bool placementChanged,
|
bool placementChanged,
|
||||||
double frameTime) override {
|
double frameEncodingTime,
|
||||||
|
double frameRenderingTime) override {
|
||||||
if (mode == RenderMode::Full && hasPendingStillImageRequest) {
|
if (mode == RenderMode::Full && hasPendingStillImageRequest) {
|
||||||
stillImage = frontend.readStillImage();
|
stillImage = frontend.readStillImage();
|
||||||
}
|
}
|
||||||
rendererObserver->onDidFinishRenderingFrame(mode, repaintNeeded, placementChanged, frameTime);
|
rendererObserver->onDidFinishRenderingFrame(
|
||||||
|
mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStyleImageMissing(const std::string& id, const StyleImageMissingCallback& done) override {
|
void onStyleImageMissing(const std::string& id, const StyleImageMissingCallback& done) override {
|
||||||
|
|||||||
@@ -2478,9 +2478,9 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
|||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fullyRendered frameTime:(double)frameTime {
|
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fullyRendered frameEncodingTime:(double)frameEncodingTime frameRenderingTime:(double)frameRenderingTime {
|
||||||
if (self.frameTimeGraphEnabled) {
|
if (self.frameTimeGraphEnabled) {
|
||||||
[self.frameTimeGraphView updatePathWithFrameDuration:frameTime];
|
[self.frameTimeGraphView updatePathWithFrameDuration:frameEncodingTime];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ load("//platform/ios/bazel:macros.bzl", "info_plist")
|
|||||||
filegroup(
|
filegroup(
|
||||||
name = "ios_benchmark_srcs",
|
name = "ios_benchmark_srcs",
|
||||||
srcs = [
|
srcs = [
|
||||||
"main.m",
|
"main.mm",
|
||||||
"MBXBenchAppDelegate.m",
|
"MBXBenchAppDelegate.mm",
|
||||||
"MBXBenchViewController.mm",
|
"MBXBenchViewController.mm",
|
||||||
"locations.cpp",
|
"locations.cpp",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,15 +1,52 @@
|
|||||||
#import "MBXBenchViewController.h"
|
#import "MBXBenchViewController.h"
|
||||||
#import "MBXBenchAppDelegate.h"
|
#import "MBXBenchAppDelegate.h"
|
||||||
#import "MLNMapView_Private.h"
|
#import "MLNMapView_Private.h"
|
||||||
#import "MLNMapViewDelegate.h"
|
#import "MLNOfflineStorage_Private.h"
|
||||||
|
#import "MLNSettings_Private.h"
|
||||||
|
|
||||||
#include "locations.hpp"
|
#include "locations.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
@interface MBXBenchViewController () <MLNMapViewDelegate>
|
#include <mbgl/gfx/backend_scope.hpp>
|
||||||
|
#include <mbgl/gfx/headless_frontend.hpp>
|
||||||
|
#include <mbgl/map/map.hpp>
|
||||||
|
#include <mbgl/style/style.hpp>
|
||||||
|
|
||||||
@property (nonatomic) MLNMapView *mapView;
|
@protocol BenchMapDelegate <NSObject>
|
||||||
|
- (void)mapDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||||
|
frameEncodingTime:(double)frameEncodingTime
|
||||||
|
frameRenderingTime:(double)frameRenderingTime;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
class BenchMapObserver : public mbgl::MapObserver {
|
||||||
|
public:
|
||||||
|
BenchMapObserver() = delete;
|
||||||
|
BenchMapObserver(id<BenchMapDelegate> mapDelegate_) : mapDelegate(mapDelegate_) {}
|
||||||
|
virtual ~BenchMapObserver() = default;
|
||||||
|
|
||||||
|
void onDidFinishRenderingFrame(RenderFrameStatus status) override final {
|
||||||
|
//NSLog(@"Frame encoding time: %4.1f ms", status.frameEncodingTime * 1e3);
|
||||||
|
//NSLog(@"Frame rendering time: %4.1f ms", status.frameRenderingTime * 1e3);
|
||||||
|
|
||||||
|
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
||||||
|
[mapDelegate mapDidFinishRenderingFrameFullyRendered:fullyRendered
|
||||||
|
frameEncodingTime:status.frameEncodingTime
|
||||||
|
frameRenderingTime:status.frameRenderingTime];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
__weak id<BenchMapDelegate> mapDelegate = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface MBXBenchViewController () <BenchMapDelegate> {
|
||||||
|
std::unique_ptr<BenchMapObserver> observer;
|
||||||
|
std::unique_ptr<mbgl::HeadlessFrontend> frontend;
|
||||||
|
std::unique_ptr<mbgl::Map> map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (nonatomic) UIImageView *imageView;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -32,6 +69,15 @@
|
|||||||
- (void)viewDidLoad
|
- (void)viewDidLoad
|
||||||
{
|
{
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidAppear:(BOOL)animated
|
||||||
|
{
|
||||||
|
[super viewDidAppear:animated];
|
||||||
|
|
||||||
|
#ifdef LOG_TO_DOCUMENTS_DIR
|
||||||
|
[self setAndRedirectLogFileToDocuments];
|
||||||
|
#endif
|
||||||
|
|
||||||
// Use a local style and local assets if they’ve been downloaded.
|
// Use a local style and local assets if they’ve been downloaded.
|
||||||
NSURL *tile = [[NSBundle mainBundle] URLForResource:@"11" withExtension:@"pbf" subdirectory:@"tiles/tiles/v3/5/7"];
|
NSURL *tile = [[NSBundle mainBundle] URLForResource:@"11" withExtension:@"pbf" subdirectory:@"tiles/tiles/v3/5/7"];
|
||||||
@@ -42,28 +88,50 @@
|
|||||||
constexpr auto styleIndex = 0;
|
constexpr auto styleIndex = 0;
|
||||||
NSURL *url = [NSURL URLWithString:tile ? @"asset://styles/streets.json" : [NSString stringWithCString:styles[styleIndex].c_str() encoding:NSUTF8StringEncoding]];
|
NSURL *url = [NSURL URLWithString:tile ? @"asset://styles/streets.json" : [NSString stringWithCString:styles[styleIndex].c_str() encoding:NSUTF8StringEncoding]];
|
||||||
NSLog(@"Using style URL: \"%@\"", [url absoluteString]);
|
NSLog(@"Using style URL: \"%@\"", [url absoluteString]);
|
||||||
|
|
||||||
|
self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
|
||||||
|
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||||
|
[self.view addSubview:self.imageView];
|
||||||
|
|
||||||
self.mapView = [[MLNMapView alloc] initWithFrame:self.view.bounds styleURL:url];
|
mbgl::Size viewSize = { static_cast<uint32_t>(self.view.bounds.size.width),
|
||||||
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
static_cast<uint32_t>(self.view.bounds.size.height) };
|
||||||
self.mapView.delegate = self;
|
auto pixelRatio = [[UIScreen mainScreen] scale];
|
||||||
self.mapView.opaque = YES;
|
|
||||||
self.mapView.zoomEnabled = NO;
|
observer = std::make_unique<BenchMapObserver>(self);
|
||||||
self.mapView.scrollEnabled = NO;
|
frontend = std::make_unique<mbgl::HeadlessFrontend>(
|
||||||
self.mapView.rotateEnabled = NO;
|
viewSize,
|
||||||
self.mapView.userInteractionEnabled = YES;
|
pixelRatio,
|
||||||
self.mapView.preferredFramesPerSecond = MLNMapViewPreferredFramesPerSecondMaximum;
|
mbgl::gfx::HeadlessBackend::SwapBehaviour::Flush,
|
||||||
|
mbgl::gfx::ContextMode::Unique,
|
||||||
[self.view addSubview:self.mapView];
|
/* localFontFamily */ std::nullopt,
|
||||||
}
|
/* invalidateOnUpdate */ false
|
||||||
|
);
|
||||||
- (void)viewDidAppear:(BOOL)animated
|
|
||||||
{
|
mbgl::MapOptions mapOptions;
|
||||||
[super viewDidAppear:animated];
|
mapOptions.withMapMode(mbgl::MapMode::Continuous)
|
||||||
|
.withSize(viewSize)
|
||||||
|
.withPixelRatio(pixelRatio)
|
||||||
|
.withConstrainMode(mbgl::ConstrainMode::None)
|
||||||
|
.withViewportMode(mbgl::ViewportMode::Default)
|
||||||
|
.withCrossSourceCollisions(true);
|
||||||
|
|
||||||
|
mbgl::TileServerOptions* tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal];
|
||||||
|
mbgl::ResourceOptions resourceOptions;
|
||||||
|
resourceOptions.withCachePath(MLNOfflineStorage.sharedOfflineStorage.databasePath.UTF8String)
|
||||||
|
.withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String)
|
||||||
|
.withTileServerOptions(*tileServerOptions);
|
||||||
|
mbgl::ClientOptions clientOptions;
|
||||||
|
|
||||||
|
auto apiKey = [[MLNSettings sharedSettings] apiKey];
|
||||||
|
if (apiKey) {
|
||||||
|
resourceOptions.withApiKey([apiKey UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
map = std::make_unique<mbgl::Map>(*frontend, *observer, mapOptions, resourceOptions, clientOptions);
|
||||||
|
map->setSize(viewSize);
|
||||||
|
map->setDebug(mbgl::MapDebugOptions::NoDebug);
|
||||||
|
map->getStyle().loadURL([url.absoluteString UTF8String]);
|
||||||
|
|
||||||
#ifdef LOG_TO_DOCUMENTS_DIR
|
|
||||||
[self setAndRedirectLogFileToDocuments];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[self startBenchmarkIteration];
|
[self startBenchmarkIteration];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +178,10 @@ NSDate* const currentDate = [NSDate date];
|
|||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
enum class State { None, WaitingForAssets, WarmingUp, Benchmarking } state = State::None;
|
enum class State { None, WaitingForAssets, WarmingUp, Benchmarking } state = State::None;
|
||||||
int frames = 0;
|
int frames = 0;
|
||||||
double totalFrameTime = 0;
|
double totalFrameEncodingTime = 0;
|
||||||
|
double totalFrameRenderingTime = 0;
|
||||||
std::chrono::steady_clock::time_point started;
|
std::chrono::steady_clock::time_point started;
|
||||||
std::vector<std::pair<std::string, double> > result;
|
std::vector<std::pair<std::string, std::pair<double, double>> > result;
|
||||||
|
|
||||||
static const int warmupDuration = 20; // frames
|
static const int warmupDuration = 20; // frames
|
||||||
static const int benchmarkDuration = 200; // frames
|
static const int benchmarkDuration = 200; // frames
|
||||||
@@ -121,31 +190,70 @@ namespace mbgl {
|
|||||||
extern std::size_t uploadCount, uploadBuildCount, uploadVertextAttrsDirty, uploadInvalidSegments;
|
extern std::size_t uploadCount, uploadBuildCount, uploadVertextAttrsDirty, uploadInvalidSegments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)renderFrame
|
||||||
|
{
|
||||||
|
mbgl::gfx::BackendScope guard{*(frontend->getBackend())};
|
||||||
|
|
||||||
|
frontend->renderFrame();
|
||||||
|
|
||||||
|
auto image = frontend->readStillImage();
|
||||||
|
|
||||||
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||||
|
CGContextRef bitmapContext = CGBitmapContextCreate(
|
||||||
|
image.data.get(),
|
||||||
|
image.size.width,
|
||||||
|
image.size.height,
|
||||||
|
8,
|
||||||
|
4 * image.size.width,
|
||||||
|
colorSpace,
|
||||||
|
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault
|
||||||
|
);
|
||||||
|
CFRelease(colorSpace);
|
||||||
|
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
|
||||||
|
CGContextRelease(bitmapContext);
|
||||||
|
|
||||||
|
self.imageView.image = [UIImage imageWithCGImage:cgImage];
|
||||||
|
CGImageRelease(cgImage);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)startBenchmarkIteration
|
- (void)startBenchmarkIteration
|
||||||
{
|
{
|
||||||
if (mbgl::bench::locations.size() > idx) {
|
if (mbgl::bench::locations.size() > idx) {
|
||||||
const auto& location = mbgl::bench::locations[idx];
|
const auto& location = mbgl::bench::locations[idx];
|
||||||
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(location.latitude, location.longitude) zoomLevel:location.zoom animated:NO];
|
|
||||||
self.mapView.direction = location.bearing;
|
mbgl::CameraOptions cameraOptions;
|
||||||
|
cameraOptions.center = mbgl::LatLng(location.latitude, location.longitude);
|
||||||
|
cameraOptions.zoom = location.zoom;
|
||||||
|
cameraOptions.bearing = location.bearing;
|
||||||
|
map->jumpTo(cameraOptions);
|
||||||
|
|
||||||
state = State::WaitingForAssets;
|
state = State::WaitingForAssets;
|
||||||
NSLog(@"Benchmarking \"%s\"", location.name.c_str());
|
NSLog(@"Benchmarking \"%s\"", location.name.c_str());
|
||||||
NSLog(@"- Loading assets...");
|
NSLog(@"- Loading assets...");
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self renderFrame];
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Do nothing. The benchmark is completed.
|
// Do nothing. The benchmark is completed.
|
||||||
NSLog(@"Benchmark completed.");
|
NSLog(@"Benchmark completed.");
|
||||||
|
|
||||||
NSLog(@"Result:");
|
NSLog(@"Result:");
|
||||||
double totalFrameTime = 0;
|
|
||||||
size_t colWidth = 0;
|
size_t colWidth = 0;
|
||||||
for (const auto& row : result) {
|
for (const auto& row : result) {
|
||||||
colWidth = std::max(row.first.size(), colWidth);
|
colWidth = std::max(row.first.size(), colWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double totalFrameEncodingTime = 0;
|
||||||
|
double totalFrameRenderingTime = 0;
|
||||||
for (const auto& row : result) {
|
for (const auto& row : result) {
|
||||||
NSLog(@"| %-*s | %4.1f ms | %4.1f fps |", int(colWidth), row.first.c_str(), 1e3 * row.second, 1.0 / row.second);
|
NSLog(@"| %-*s | %4.1f ms | %4.1f ms |", int(colWidth), row.first.c_str(), 1e3 * row.second.first, 1e3 * row.second.second);
|
||||||
totalFrameTime += row.second;
|
totalFrameEncodingTime += row.second.first;
|
||||||
|
totalFrameRenderingTime += row.second.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLog(@"Average frame time: %4.1f ms", totalFrameTime * 1e3 / result.size());
|
NSLog(@"Average frame encoding time: %4.1f ms", totalFrameEncodingTime * 1e3 / result.size());
|
||||||
NSLog(@"Average FPS: %4.1f", result.size() / totalFrameTime);
|
NSLog(@"Average frame rendering time: %4.1f ms", totalFrameRenderingTime * 1e3 / result.size());
|
||||||
|
|
||||||
// NSLog(@"Total uploads: %zu", mbgl::uploadCount);
|
// NSLog(@"Total uploads: %zu", mbgl::uploadCount);
|
||||||
// NSLog(@"Total uploads with dirty vattr: %zu", mbgl::uploadVertextAttrsDirty);
|
// NSLog(@"Total uploads with dirty vattr: %zu", mbgl::uploadVertextAttrsDirty);
|
||||||
@@ -165,21 +273,18 @@ namespace mbgl {
|
|||||||
{
|
{
|
||||||
[app performSelector:@selector(suspend)];
|
[app performSelector:@selector(suspend)];
|
||||||
}
|
}
|
||||||
else if ([app respondsToSelector:@selector(terminate)])
|
|
||||||
{
|
|
||||||
[app performSelector:@selector(terminate)];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
- (void)mapDidFinishRenderingFrameFullyRendered:(__unused BOOL)fullyRendered
|
||||||
fullyRendered:(__unused BOOL)fullyRendered
|
frameEncodingTime:(double)frameEncodingTime
|
||||||
frameTime:(double)frameTime
|
frameRenderingTime:(double)frameRenderingTime
|
||||||
{
|
{
|
||||||
if (state == State::Benchmarking)
|
if (state == State::Benchmarking)
|
||||||
{
|
{
|
||||||
frames++;
|
frames++;
|
||||||
totalFrameTime += frameTime;
|
totalFrameEncodingTime += frameEncodingTime;
|
||||||
|
totalFrameRenderingTime += frameRenderingTime;
|
||||||
if (frames >= benchmarkDuration)
|
if (frames >= benchmarkDuration)
|
||||||
{
|
{
|
||||||
state = State::None;
|
state = State::None;
|
||||||
@@ -187,45 +292,49 @@ namespace mbgl {
|
|||||||
// Report FPS
|
// Report FPS
|
||||||
const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - started).count();
|
const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - started).count();
|
||||||
const auto wallClockFPS = double(frames * 1e6) / duration;
|
const auto wallClockFPS = double(frames * 1e6) / duration;
|
||||||
const auto frameTime = static_cast<double>(totalFrameTime) / frames;
|
const auto frameEncodingTime = static_cast<double>(totalFrameEncodingTime) / frames;
|
||||||
const auto potentialFPS = 1.0 / frameTime;
|
const auto frameRenderingTime = static_cast<double>(totalFrameRenderingTime) / frames;
|
||||||
result.emplace_back(mbgl::bench::locations[idx].name, frameTime);
|
result.emplace_back(mbgl::bench::locations[idx].name, std::make_pair(frameEncodingTime, frameRenderingTime));
|
||||||
NSLog(@"- Frame time: %.1f ms, FPS: %.1f (%.1f sync FPS)", frameTime * 1e3, potentialFPS, wallClockFPS);
|
NSLog(@"- Frame encoding time: %.1f ms, Frame rendering time: %.1f ms (%.1f sync FPS)", frameEncodingTime * 1e3, frameRenderingTime * 1e3, wallClockFPS);
|
||||||
|
|
||||||
// Start benchmarking the next location.
|
// Start benchmarking the next location.
|
||||||
idx++;
|
idx++;
|
||||||
[self startBenchmarkIteration];
|
[self startBenchmarkIteration];
|
||||||
} else {
|
} else {
|
||||||
[mapView setNeedsRerender];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self renderFrame];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (state == State::WarmingUp)
|
else if (state == State::WarmingUp)
|
||||||
{
|
{
|
||||||
frames++;
|
frames++;
|
||||||
if (frames >= warmupDuration)
|
if (frames >= warmupDuration)
|
||||||
{
|
{
|
||||||
frames = 0;
|
frames = 0;
|
||||||
totalFrameTime = 0;
|
totalFrameEncodingTime = 0;
|
||||||
|
totalFrameRenderingTime = 0;
|
||||||
state = State::Benchmarking;
|
state = State::Benchmarking;
|
||||||
started = std::chrono::steady_clock::now();
|
started = std::chrono::steady_clock::now();
|
||||||
NSLog(@"- Benchmarking for %d frames...", benchmarkDuration);
|
NSLog(@"- Benchmarking for %d frames...", benchmarkDuration);
|
||||||
}
|
}
|
||||||
[mapView setNeedsRerender];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self renderFrame];
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (state == State::WaitingForAssets)
|
else if (state == State::WaitingForAssets)
|
||||||
{
|
{
|
||||||
if ([mapView isFullyLoaded])
|
if (map->isFullyLoaded())
|
||||||
{
|
{
|
||||||
// Start the benchmarking timer.
|
// Start the benchmarking timer.
|
||||||
state = State::WarmingUp;
|
state = State::WarmingUp;
|
||||||
[self.mapView didReceiveMemoryWarning];
|
|
||||||
NSLog(@"- Warming up for %d frames...", warmupDuration);
|
NSLog(@"- Warming up for %d frames...", warmupDuration);
|
||||||
[mapView setNeedsRerender];
|
|
||||||
}
|
}
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self renderFrame];
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ void MLNMapViewImpl::onWillStartRenderingFrame() {
|
|||||||
|
|
||||||
void MLNMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status) {
|
void MLNMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status) {
|
||||||
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
|
||||||
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered frameTime:status.frameTime];
|
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered frameEncodingTime:status.frameEncodingTime frameRenderingTime:status.frameRenderingTime];
|
||||||
}
|
}
|
||||||
|
|
||||||
void MLNMapViewImpl::onWillStartRenderingMap() {
|
void MLNMapViewImpl::onWillStartRenderingMap() {
|
||||||
|
|||||||
@@ -6813,7 +6813,8 @@ static void *windowScreenContext = &windowScreenContext;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||||
frameTime:(double)frameTime {
|
frameEncodingTime:(double)frameEncodingTime
|
||||||
|
frameRenderingTime:(double)frameRenderingTime {
|
||||||
if (!_mbglMap)
|
if (!_mbglMap)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -6825,9 +6826,9 @@ static void *windowScreenContext = &windowScreenContext;
|
|||||||
[self.style didChangeValueForKey:@"layers"];
|
[self.style didChangeValueForKey:@"layers"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:frameTime:)])
|
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:frameEncodingTime:frameRenderingTime:)])
|
||||||
{
|
{
|
||||||
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered frameTime:frameTime];
|
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered frameEncodingTime:frameEncodingTime frameRenderingTime:frameRenderingTime];
|
||||||
}
|
}
|
||||||
else if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)])
|
else if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -254,7 +254,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView
|
||||||
fullyRendered:(BOOL)fullyRendered
|
fullyRendered:(BOOL)fullyRendered
|
||||||
frameTime:(double)frameTime;
|
frameEncodingTime:(double)frameEncodingTime
|
||||||
|
frameRenderingTime:(double)frameRenderingTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Tells the delegate that the map view is entering an idle state, and no more
|
Tells the delegate that the map view is entering an idle state, and no more
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ FOUNDATION_EXTERN MLN_EXPORT MLNExceptionName const _Nonnull MLNUnderlyingMapUna
|
|||||||
- (void)mapViewDidFailLoadingMapWithError:(nonnull NSError *)error;
|
- (void)mapViewDidFailLoadingMapWithError:(nonnull NSError *)error;
|
||||||
- (void)mapViewWillStartRenderingFrame;
|
- (void)mapViewWillStartRenderingFrame;
|
||||||
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered
|
||||||
frameTime:(double)frameTime;
|
frameEncodingTime:(double)frameEncodingTime
|
||||||
|
frameRenderingTime:(double)frameRenderingTime;
|
||||||
- (void)mapViewWillStartRenderingMap;
|
- (void)mapViewWillStartRenderingMap;
|
||||||
- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered;
|
- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered;
|
||||||
- (void)mapViewDidBecomeIdle;
|
- (void)mapViewDidBecomeIdle;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ extension MLNMapViewDelegateIntegrationTests: MLNMapViewDelegate {
|
|||||||
|
|
||||||
func mapViewDidFinishRenderingFrame(_ mapView: MLNMapView, fullyRendered: Bool) {}
|
func mapViewDidFinishRenderingFrame(_ mapView: MLNMapView, fullyRendered: Bool) {}
|
||||||
|
|
||||||
func mapViewDidFinishRenderingFrame(_ mapView: MLNMapView, fullyRendered: Bool, frameTime: Double) {}
|
func mapViewDidFinishRenderingFrame(_ mapView: MLNMapView, fullyRendered: Bool, frameEncodingTime: Double, frameRenderingTime: Double) {}
|
||||||
|
|
||||||
func mapView(_ mapView: MLNMapView, shapeAnnotationIsEnabled annotation: MLNShape) -> Bool { return false }
|
func mapView(_ mapView: MLNMapView, shapeAnnotationIsEnabled annotation: MLNShape) -> Bool { return false }
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,11 @@ public:
|
|||||||
void onDidFinishRenderingFrame(RenderMode mode,
|
void onDidFinishRenderingFrame(RenderMode mode,
|
||||||
bool repaintNeeded,
|
bool repaintNeeded,
|
||||||
bool placementChanged,
|
bool placementChanged,
|
||||||
double frameTime) override {
|
double frameEncodingTime,
|
||||||
|
double frameRenderingTime) override {
|
||||||
void (mbgl::RendererObserver::*f)(
|
void (mbgl::RendererObserver::*f)(
|
||||||
RenderMode, bool, bool, double) = &mbgl::RendererObserver::onDidFinishRenderingFrame;
|
RenderMode, bool, bool, double, double) = &mbgl::RendererObserver::onDidFinishRenderingFrame;
|
||||||
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameTime);
|
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDidFinishRenderingMap() final { delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingMap); }
|
void onDidFinishRenderingMap() final { delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingMap); }
|
||||||
|
|||||||
@@ -131,12 +131,16 @@ void Map::Impl::onWillStartRenderingFrame() {
|
|||||||
void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode,
|
void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode,
|
||||||
bool needsRepaint,
|
bool needsRepaint,
|
||||||
bool placemenChanged,
|
bool placemenChanged,
|
||||||
double frameTime) {
|
double frameEncodingTime,
|
||||||
|
double frameRenderingTime) {
|
||||||
rendererFullyLoaded = renderMode == RenderMode::Full;
|
rendererFullyLoaded = renderMode == RenderMode::Full;
|
||||||
|
|
||||||
if (mode == MapMode::Continuous) {
|
if (mode == MapMode::Continuous) {
|
||||||
observer.onDidFinishRenderingFrame(
|
observer.onDidFinishRenderingFrame({MapObserver::RenderMode(renderMode),
|
||||||
{MapObserver::RenderMode(renderMode), needsRepaint, placemenChanged, frameTime});
|
needsRepaint,
|
||||||
|
placemenChanged,
|
||||||
|
frameEncodingTime,
|
||||||
|
frameRenderingTime});
|
||||||
|
|
||||||
if (needsRepaint || transform.inTransition()) {
|
if (needsRepaint || transform.inTransition()) {
|
||||||
onUpdate();
|
onUpdate();
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
void onInvalidate() final;
|
void onInvalidate() final;
|
||||||
void onResourceError(std::exception_ptr) final;
|
void onResourceError(std::exception_ptr) final;
|
||||||
void onWillStartRenderingFrame() final;
|
void onWillStartRenderingFrame() final;
|
||||||
void onDidFinishRenderingFrame(RenderMode, bool, bool, double) final;
|
void onDidFinishRenderingFrame(RenderMode, bool, bool, double, double) final;
|
||||||
void onWillStartRenderingMap() final;
|
void onWillStartRenderingMap() final;
|
||||||
void onDidFinishRenderingMap() final;
|
void onDidFinishRenderingMap() final;
|
||||||
void onStyleImageMissing(const std::string&, const std::function<void()>&) final;
|
void onStyleImageMissing(const std::string&, const std::function<void()>&) final;
|
||||||
|
|||||||
@@ -382,16 +382,22 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
|||||||
|
|
||||||
// Ends the RenderPass
|
// Ends the RenderPass
|
||||||
parameters.renderPass.reset();
|
parameters.renderPass.reset();
|
||||||
|
|
||||||
|
const auto startRendering = util::MonotonicTimer::now().count();
|
||||||
parameters.encoder->present(parameters.backend.getDefaultRenderable());
|
parameters.encoder->present(parameters.backend.getDefaultRenderable());
|
||||||
|
const auto renderingTime = util::MonotonicTimer::now().count() - startRendering;
|
||||||
|
|
||||||
// CommandEncoder destructor submits render commands.
|
// CommandEncoder destructor submits render commands.
|
||||||
parameters.encoder.reset();
|
parameters.encoder.reset();
|
||||||
|
|
||||||
|
const auto encodingTime = renderTree.getElapsedTime() - renderingTime;
|
||||||
|
|
||||||
observer->onDidFinishRenderingFrame(
|
observer->onDidFinishRenderingFrame(
|
||||||
renderTreeParameters.loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
|
renderTreeParameters.loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
|
||||||
renderTreeParameters.needsRepaint,
|
renderTreeParameters.needsRepaint,
|
||||||
renderTreeParameters.placementChanged,
|
renderTreeParameters.placementChanged,
|
||||||
renderTree.getElapsedTime());
|
encodingTime,
|
||||||
|
renderingTime);
|
||||||
|
|
||||||
if (!renderTreeParameters.loaded) {
|
if (!renderTreeParameters.loaded) {
|
||||||
renderState = RenderState::Partial;
|
renderState = RenderState::Partial;
|
||||||
|
|||||||
Reference in New Issue
Block a user