Files
MapLibreParticleLab/ios/benchmark/MBXBenchViewController.mm
Minh Nguyễn 6fcb9f4da1 [ios] Invalidate CADisplayLink
CADisplayLink holds a strong reference to its target, forming a cycle that must be broken with -[CADisplayLink invalidate] when the animation is complete. I don’t yet have enough faith that will-change and did-change notifications are always coming from mbgl in pairs, so this change limits CADisplayLink to when MGLMapView is in the view hierarchy. It also pauses the CADisplayLink when the view is hidden or the application is in the background. Finally, -[MGLMapView invalidate] has been renamed because that term tends not to mean “redraw” in Cocoa but is rather tied to timers.

Fixes #3130.

[ios] Also invalidate CADisplayLink on removal from window

[ios] Also shut down CADisplayLink when view is hidden
2016-01-07 20:28:44 -08:00

161 lines
4.4 KiB
Plaintext

#import "MBXBenchViewController.h"
#import <mbgl/ios/MGLMapView.h>
#include "locations.hpp"
#include <chrono>
@interface MGLMapView (MBXBenchmarkAdditions)
#pragma mark - Debugging
/** Triggers another render pass even when it is not necessary. */
- (void)setNeedsGLDisplay;
/** Returns whether the map view is currently loading or processing any assets required to render the map */
- (BOOL)isFullyLoaded;
@end
@interface MBXBenchViewController () <MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
@end
@implementation MBXBenchViewController
#pragma mark - Setup
+ (void)initialize
{
if (self == [MBXBenchViewController class])
{
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
@"MBXUserTrackingMode": @(MGLUserTrackingModeNone),
@"MBXShowsUserLocation": @NO,
@"MBXDebug": @NO,
}];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL* url = [[NSURL alloc] initWithString:@"mapbox://styles/mapbox/streets-v8.json"];
self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds styleURL:url];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.mapView.delegate = self;
self.mapView.zoomEnabled = NO;
self.mapView.scrollEnabled = NO;
self.mapView.rotateEnabled = NO;
self.mapView.userInteractionEnabled = YES;
[self.mapView setDebugActive:NO];
[self startBenchmarkIteration];
[self.view addSubview:self.mapView];
}
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
template <typename Duration>
inline int64_t Microseconds(Duration d) {
return std::chrono::duration_cast<std::chrono::microseconds>(d).count();
}
size_t idx = 0;
enum class State { None, WaitingForAssets, WarmingUp, Benchmarking } state = State::None;
int frames = 0;
TimePoint started;
std::vector<std::pair<std::string, double>> result;
static const int warmupDuration = 20; // frames
static const int benchmarkDuration = 200; // frames
- (void)startBenchmarkIteration
{
if (mbgl::bench::locations.size() > 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;
state = State::WaitingForAssets;
NSLog(@"Benchmarking \"%s\"", location.name.c_str());
NSLog(@"- Loading assets...");
} else {
// Do nothing. The benchmark is completed.
NSLog(@"Benchmark completed.");
NSLog(@"Result:");
size_t colWidth = 0;
for (const auto& row : result) {
colWidth = std::max(row.first.size(), colWidth);
}
for (const auto& row : result) {
NSLog(@"| %-*s | %4.1f fps |", int(colWidth), row.first.c_str(), row.second);
}
exit(0);
}
}
- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(__unused BOOL)fullyRendered
{
if (state == State::Benchmarking)
{
frames++;
if (frames >= benchmarkDuration)
{
state = State::None;
// Report FPS
const auto duration = Microseconds(Clock::now() - started);
const auto fps = double(frames * 1e6) / duration;
result.emplace_back(mbgl::bench::locations[idx].name, fps);
NSLog(@"- FPS: %.1f", fps);
// Start benchmarking the next location.
idx++;
[self startBenchmarkIteration];
} else {
[mapView setNeedsGLDisplay];
}
return;
}
else if (state == State::WarmingUp)
{
frames++;
if (frames >= warmupDuration)
{
frames = 0;
state = State::Benchmarking;
started = Clock::now();
NSLog(@"- Benchmarking for %d frames...", benchmarkDuration);
}
[mapView setNeedsGLDisplay];
return;
}
else if (state == State::WaitingForAssets)
{
if ([mapView isFullyLoaded])
{
// Start the benchmarking timer.
state = State::WarmingUp;
[self.mapView emptyMemoryCache];
NSLog(@"- Warming up for %d frames...", warmupDuration);
[mapView setNeedsGLDisplay];
}
return;
}
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
@end