diff options
24 files changed, 971 insertions, 576 deletions
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.mm b/platform/darwin/src/MGLOpenGLStyleLayer.mm index 945348cb03..b010e363b3 100644 --- a/platform/darwin/src/MGLOpenGLStyleLayer.mm +++ b/platform/darwin/src/MGLOpenGLStyleLayer.mm @@ -191,7 +191,7 @@ private: causing the `-drawInMapView:withContext:` method to be called. */ - (void)setNeedsDisplay { - [self.style.mapView setNeedsGLDisplay]; + [self.style.mapView setNeedsRerender]; } @end diff --git a/platform/darwin/src/MGLRendererFrontend.h b/platform/darwin/src/MGLRendererFrontend.h index ead6ad2660..1358f5fafa 100644 --- a/platform/darwin/src/MGLRendererFrontend.h +++ b/platform/darwin/src/MGLRendererFrontend.h @@ -1,7 +1,7 @@ #include <mbgl/gfx/backend_scope.hpp> #include <mbgl/renderer/renderer.hpp> #include <mbgl/renderer/renderer_frontend.hpp> -#include <mbgl/gl/renderer_backend.hpp> +#include <mbgl/gfx/renderer_backend.hpp> #include <mbgl/util/async_task.hpp> #include <mbgl/util/optional.hpp> @@ -13,13 +13,13 @@ class MGLRenderFrontend : public mbgl::RendererFrontend { public: - MGLRenderFrontend(std::unique_ptr<mbgl::Renderer> renderer_, MGLMapView* nativeView_, mbgl::gl::RendererBackend& mbglBackend_, bool async = false) + MGLRenderFrontend(std::unique_ptr<mbgl::Renderer> renderer_, MGLMapView* nativeView_, mbgl::gfx::RendererBackend& mbglBackend_, bool async = false) : renderer(std::move(renderer_)) , nativeView(nativeView_) , mbglBackend(mbglBackend_) { if (async) { asyncInvalidate.emplace([&]() { - [nativeView setNeedsGLDisplay]; + [nativeView setNeedsRerender]; }); } } @@ -35,7 +35,7 @@ public: if (asyncInvalidate) { asyncInvalidate->send(); } else { - [nativeView setNeedsGLDisplay]; + [nativeView setNeedsRerender]; } } @@ -69,7 +69,7 @@ public: private: std::unique_ptr<mbgl::Renderer> renderer; __weak MGLMapView *nativeView = nullptr; - mbgl::gl::RendererBackend& mbglBackend; + mbgl::gfx::RendererBackend& mbglBackend; std::shared_ptr<mbgl::UpdateParameters> updateParameters; mbgl::optional<mbgl::util::AsyncTask> asyncInvalidate; }; diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.mm index a21bcad9d0..4d13f6f705 100644 --- a/platform/darwin/src/headless_backend_cgl.cpp +++ b/platform/darwin/src/headless_backend_cgl.mm @@ -2,6 +2,7 @@ #include <mbgl/util/logging.hpp> #include <OpenGL/OpenGL.h> +#include <Foundation/NSString.h> #include <CoreFoundation/CoreFoundation.h> #include <string> @@ -92,12 +93,8 @@ public: throw std::runtime_error("Failed to load OpenGL framework."); } - CFStringRef str = - CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void* symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast<gl::ProcAddress>(symbol); + return reinterpret_cast<gl::ProcAddress>(CFBundleGetFunctionPointerForName( + framework, (__bridge CFStringRef)[NSString stringWithUTF8String:name])); } void activateContext() final { diff --git a/platform/darwin/src/headless_backend_eagl.mm b/platform/darwin/src/headless_backend_eagl.mm index 4ccc485313..f2c7d8c2fb 100644 --- a/platform/darwin/src/headless_backend_eagl.mm +++ b/platform/darwin/src/headless_backend_eagl.mm @@ -26,12 +26,8 @@ public: throw std::runtime_error("Failed to load OpenGL framework."); } - CFStringRef str = - CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void* symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast<gl::ProcAddress>(symbol); + return reinterpret_cast<gl::ProcAddress>(CFBundleGetFunctionPointerForName( + framework, (__bridge CFStringRef)[NSString stringWithUTF8String:name])); } void activateContext() final { diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 719a1d6974..0bdb3706dd 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -269,9 +269,18 @@ 40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; }; + 550570C622958FB400228ECF /* MGLMapView+Impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 550570C422958FB300228ECF /* MGLMapView+Impl.mm */; }; + 550570C722958FB400228ECF /* MGLMapView+Impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 550570C422958FB300228ECF /* MGLMapView+Impl.mm */; }; + 550570C822958FB400228ECF /* MGLMapView+Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 550570C522958FB400228ECF /* MGLMapView+Impl.h */; }; + 550570C922958FB400228ECF /* MGLMapView+Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 550570C522958FB400228ECF /* MGLMapView+Impl.h */; }; + 550570D22296E96E00228ECF /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA27C24D1CBB3811000B0ECD /* GLKit.framework */; }; 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D71E1D085500E2C41B /* MGLVersionNumber.m */; }; 556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5580B45B229570A10091291B /* MGLMapView+OpenGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 5580B459229570A00091291B /* MGLMapView+OpenGL.h */; }; + 5580B45C229570A10091291B /* MGLMapView+OpenGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 5580B459229570A00091291B /* MGLMapView+OpenGL.h */; }; + 5580B45D229570A10091291B /* MGLMapView+OpenGL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */; }; + 5580B45E229570A10091291B /* MGLMapView+OpenGL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */; }; 558DE7A01E5615E400C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */; }; 558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */; }; 558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; }; @@ -1009,9 +1018,13 @@ 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource_Private.h; sourceTree = "<group>"; }; 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; }; 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; }; + 550570C422958FB300228ECF /* MGLMapView+Impl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+Impl.mm"; sourceTree = "<group>"; }; + 550570C522958FB400228ECF /* MGLMapView+Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+Impl.h"; sourceTree = "<group>"; }; 554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 556660C91E1BF3A900E2C41B /* MGLFoundation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLFoundation.h; sourceTree = "<group>"; wrapsLines = 0; }; 556660D71E1D085500E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; }; + 5580B459229570A00091291B /* MGLMapView+OpenGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+OpenGL.h"; sourceTree = "<group>"; }; + 5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+OpenGL.mm"; sourceTree = "<group>"; }; 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; }; 558DE79F1E5615E400C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; }; 55CF752E213ED92000ED86C4 /* libicu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libicu.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1435,6 +1448,7 @@ DAAE5F8920F047240089D85B /* libmbgl-filesource.a in Frameworks */, DAAE5F8A20F0472E0089D85B /* libmbgl-loop-darwin.a in Frameworks */, 55CF7531213ED92A00ED86C4 /* libicu.a in Frameworks */, + 550570D22296E96E00228ECF /* GLKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2033,6 +2047,10 @@ DA17BE2F1CC4BAC300402C41 /* MGLMapView_Private.h */, DA8848361CBAFB8500AB86E3 /* MGLMapView.h */, DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */, + 550570C522958FB400228ECF /* MGLMapView+Impl.h */, + 550570C422958FB300228ECF /* MGLMapView+Impl.mm */, + 5580B459229570A00091291B /* MGLMapView+OpenGL.h */, + 5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */, DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */, DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */, ); @@ -2346,6 +2364,7 @@ 353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */, 6F018BB0220031BF003E7269 /* UIView+MGLAdditions.h in Headers */, 1FCAE2A820B88B3800C577DD /* MGLLocationManager_Private.h in Headers */, + 5580B45B229570A10091291B /* MGLMapView+OpenGL.h in Headers */, DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */, DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */, 1F6A82A82138871900BA5B41 /* MGLLoggingConfiguration_Private.h in Headers */, @@ -2458,6 +2477,7 @@ 55E5666321C2A2080008B8B5 /* MMEMetrics.h in Headers */, 55E5666421C2A2080008B8B5 /* MMEMetricsManager.h in Headers */, 55E5666621C2A2080008B8B5 /* MMENSURLSessionWrapper.h in Headers */, + 550570C822958FB400228ECF /* MGLMapView+Impl.h in Headers */, 55E5666721C2A2080008B8B5 /* MMETimerManager.h in Headers */, 55E5666821C2A2080008B8B5 /* MMETypes.h in Headers */, 55E5666921C2A2080008B8B5 /* MMEUIApplicationWrapper.h in Headers */, @@ -2530,6 +2550,8 @@ 96E516FA20005A3D00A02306 /* MGLUserLocationHeadingArrowLayer.h in Headers */, 96E516E62000560B00A02306 /* MGLOfflineRegion_Private.h in Headers */, DAD1656D1CF41981001FF4B9 /* MGLFeature.h in Headers */, + 550570C922958FB400228ECF /* MGLMapView+Impl.h in Headers */, + 5580B45C229570A10091291B /* MGLMapView+OpenGL.h in Headers */, 96E516E72000560B00A02306 /* MGLOfflineStorage_Private.h in Headers */, DA17BE311CC4BDAA00402C41 /* MGLMapView_Private.h in Headers */, DABFB86C1CBE99E500D62B32 /* MGLTypes.h in Headers */, @@ -3131,10 +3153,12 @@ DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */, 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */, ACD0245A2187EABA00D8C8A7 /* MMEMetricsManager.m in Sources */, + 550570C622958FB400228ECF /* MGLMapView+Impl.mm in Sources */, 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */, 40834BEB1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */, DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.mm in Sources */, + 5580B45D229570A10091291B /* MGLMapView+OpenGL.mm in Sources */, 40834BEA1FE05E1800C1BD0D /* MMEConstants.m in Sources */, AC46EB5F225E60510039C013 /* MMEPinningConfigurationProvider.m in Sources */, 929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */, @@ -3255,10 +3279,12 @@ 30E5781A1DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */, ACD0245B2187EABA00D8C8A7 /* MMEMetricsManager.m in Sources */, 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, + 550570C722958FB400228ECF /* MGLMapView+Impl.mm in Sources */, DAA4E4291CBB730400178DFB /* NSBundle+MGLAdditions.m in Sources */, 40834BFF1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */, 40834BFE1FE05E1800C1BD0D /* MMEConstants.m in Sources */, 35136D3D1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */, + 5580B45E229570A10091291B /* MGLMapView+OpenGL.mm in Sources */, DD9BE4FA1EB263F40079A3AF /* UIViewController+MGLAdditions.m in Sources */, AC46EB60225E60510039C013 /* MMEPinningConfigurationProvider.m in Sources */, 350098DF1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */, diff --git a/platform/ios/sdk-files.json b/platform/ios/sdk-files.json index 3031a87fd6..f55c126794 100644 --- a/platform/ios/sdk-files.json +++ b/platform/ios/sdk-files.json @@ -13,10 +13,12 @@ "platform/darwin/src/MGLFeature.mm", "platform/ios/src/UIImage+MGLAdditions.mm", "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEMetricsManager.m", + "platform/ios/src/MGLMapView+Impl.mm", "platform/ios/src/MGLAnnotationContainerView.m", "platform/ios/src/MGLCompactCalloutView.m", "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEDependencyManager.m", "platform/darwin/src/MGLPointAnnotation.mm", + "platform/ios/src/MGLMapView+OpenGL.mm", "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEConstants.m", "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEPinningConfigurationProvider.m", "platform/ios/src/MGLAnnotationView.mm", @@ -228,6 +230,7 @@ "MGLStyleLayer_Private.h": "platform/darwin/src/MGLStyleLayer_Private.h", "UIView+MGLAdditions.h": "platform/ios/src/UIView+MGLAdditions.h", "MGLLocationManager_Private.h": "platform/darwin/src/MGLLocationManager_Private.h", + "MGLMapView+OpenGL.h": "platform/ios/src/MGLMapView+OpenGL.h", "MGLLoggingConfiguration_Private.h": "platform/darwin/src/MGLLoggingConfiguration_Private.h", "NSComparisonPredicate+MGLAdditions.h": "platform/darwin/src/NSComparisonPredicate+MGLAdditions.h", "MGLMapAccessibilityElement.h": "platform/ios/src/MGLMapAccessibilityElement.h", @@ -295,6 +298,7 @@ "MMEMetrics.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEMetrics.h", "MMEMetricsManager.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEMetricsManager.h", "MMENSURLSessionWrapper.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMENSURLSessionWrapper.h", + "MGLMapView+Impl.h": "platform/ios/src/MGLMapView+Impl.h", "MMETimerManager.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.h", "MMETypes.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMETypes.h", "MMEUIApplicationWrapper.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEUIApplicationWrapper.h", diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 5f76dbff5c..81153eb41c 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -1,10 +1,12 @@ #import "MGLAnnotationView.h" #import "MGLAnnotationView_Private.h" #import "MGLMapView_Private.h" +#import "MGLCalloutView.h" #import "MGLAnnotation.h" #import "MGLLoggingConfiguration_Private.h" #import "NSBundle+MGLAdditions.h" +#import "NSValue+MGLAdditions.h" #include <mbgl/util/constants.hpp> diff --git a/platform/ios/src/MGLMapView+Impl.h b/platform/ios/src/MGLMapView+Impl.h new file mode 100644 index 0000000000..f9368b7f85 --- /dev/null +++ b/platform/ios/src/MGLMapView+Impl.h @@ -0,0 +1,73 @@ +#import <mbgl/gfx/renderer_backend.hpp> +#import <mbgl/map/map_observer.hpp> +#import <mbgl/util/image.hpp> + +#import <UIKit/UIView.h> +#import <UIKit/UIImage.h> +#import <QuartzCore/CALayer.h> + +@class MGLMapView; + +class MGLMapViewImpl : public mbgl::MapObserver { +public: + static std::unique_ptr<MGLMapViewImpl> Create(MGLMapView*); + + MGLMapViewImpl(MGLMapView*); + virtual ~MGLMapViewImpl() = default; + + virtual mbgl::gfx::RendererBackend& getRendererBackend() = 0; + + // Returns a handle to the OpenGL context object if this view is rendered with OpenGL. + virtual EAGLContext* getEAGLContext() { + return nullptr; + } + + // Gets called when the opaqueness of the view changes. + virtual void setOpaque(bool) = 0; + + // Triggers an immediate render of the underlying view. + virtual void display() = 0; + + // We update the transaction mode when the user adds annotation views that need to be layered on + // top of the view. + virtual void setPresentsWithTransaction(bool) = 0; + + // Called when initially creating the rendering view, and when resuming rendering after returning + // from the background. + virtual void createView() = 0; + + // We expose the underlying UIView because we need it in order to add annotation views above it. + virtual UIView* getView() = 0; + + // Called when the application goes to the background and we've replaced the interactively + // rendered map view with a static image. + virtual void deleteView() = 0; + + // called before the application goes to the background. The resulting image is used as a place + // holder instead of the regular view. This allows us to drop the framebuffers associated with + // the rendering context and reduce memory while in the background. + virtual UIImage* snapshot() = 0; + + // Called by the view delegate when it's time to render. + void render(); + + // mbgl::MapObserver implementation + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) override; + void onCameraIsChanging() override; + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) override; + void onWillStartLoadingMap() override; + void onDidFinishLoadingMap() override; + void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override; + void onWillStartRenderingFrame() override; + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) override; + void onWillStartRenderingMap() override; + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) override; + void onDidFinishLoadingStyle() override; + void onSourceChanged(mbgl::style::Source& source) override; + void onDidBecomeIdle() override; + void onStyleImageMissing(const std::string& imageIdentifier) override; + +protected: + /// Cocoa map view that this adapter bridges to. + __weak MGLMapView *mapView = nullptr; +}; diff --git a/platform/ios/src/MGLMapView+Impl.mm b/platform/ios/src/MGLMapView+Impl.mm new file mode 100644 index 0000000000..73e692defe --- /dev/null +++ b/platform/ios/src/MGLMapView+Impl.mm @@ -0,0 +1,102 @@ +#import "MGLMapView+Impl.h" +#import "MGLMapView+OpenGL.h" +#import "MGLStyle_Private.h" +#import "NSBundle+MGLAdditions.h" + +std::unique_ptr<MGLMapViewImpl> MGLMapViewImpl::Create(MGLMapView* nativeView) { + return std::make_unique<MGLMapViewOpenGLImpl>(nativeView); +} + +MGLMapViewImpl::MGLMapViewImpl(MGLMapView* nativeView_) : mapView(nativeView_) { +} + +void MGLMapViewImpl::render() { + [mapView renderSync]; +} + +void MGLMapViewImpl::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [mapView cameraWillChangeAnimated:animated]; +} + +void MGLMapViewImpl::onCameraIsChanging() { + [mapView cameraIsChanging]; +} + +void MGLMapViewImpl::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [mapView cameraDidChangeAnimated:animated]; +} + +void MGLMapViewImpl::onWillStartLoadingMap() { + [mapView mapViewWillStartLoadingMap]; +} + +void MGLMapViewImpl::onDidFinishLoadingMap() { + [mapView mapViewDidFinishLoadingMap]; +} + +void MGLMapViewImpl::onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) { + NSString *description; + MGLErrorCode code; + switch (mapError) { + case mbgl::MapLoadError::StyleParseError: + code = MGLErrorCodeParseStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description"); + break; + case mbgl::MapLoadError::StyleLoadError: + code = MGLErrorCodeLoadStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); + break; + case mbgl::MapLoadError::NotFoundError: + code = MGLErrorCodeNotFound; + description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description"); + break; + default: + code = MGLErrorCodeUnknown; + description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description"); + } + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: description, + NSLocalizedFailureReasonErrorKey: @(what.c_str()), + }; + NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo]; + [mapView mapViewDidFailLoadingMapWithError:error]; +} + +void MGLMapViewImpl::onWillStartRenderingFrame() { + [mapView mapViewWillStartRenderingFrame]; +} + +void MGLMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered]; +} + +void MGLMapViewImpl::onWillStartRenderingMap() { + [mapView mapViewWillStartRenderingMap]; +} + +void MGLMapViewImpl::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [mapView mapViewDidFinishRenderingMapFullyRendered:fullyRendered]; +} + +void MGLMapViewImpl::onDidFinishLoadingStyle() { + [mapView mapViewDidFinishLoadingStyle]; +} + +void MGLMapViewImpl::onSourceChanged(mbgl::style::Source& source) { + NSString *identifier = @(source.getID().c_str()); + MGLSource * nativeSource = [mapView.style sourceWithIdentifier:identifier]; + [mapView sourceDidChange:nativeSource]; +} + +void MGLMapViewImpl::onDidBecomeIdle() { + [mapView mapViewDidBecomeIdle]; +} + +void MGLMapViewImpl::onStyleImageMissing(const std::string& imageIdentifier) { + NSString *imageName = [NSString stringWithUTF8String:imageIdentifier.c_str()]; + [mapView didFailToLoadImage:imageName]; +} diff --git a/platform/ios/src/MGLMapView+OpenGL.h b/platform/ios/src/MGLMapView+OpenGL.h new file mode 100644 index 0000000000..12edb13113 --- /dev/null +++ b/platform/ios/src/MGLMapView+OpenGL.h @@ -0,0 +1,59 @@ +#import "MGLMapView+Impl.h" +#import "MGLMapView_Private.h" + +#include <mbgl/gfx/renderable.hpp> +#include <mbgl/gl/renderer_backend.hpp> + +@class MGLMapViewImplDelegate; + +/// Adapter responsible for bridging calls from mbgl to MGLMapView and Cocoa. +class MGLMapViewOpenGLImpl final : public MGLMapViewImpl, + public mbgl::gl::RendererBackend, + public mbgl::gfx::Renderable { +public: + MGLMapViewOpenGLImpl(MGLMapView*); + ~MGLMapViewOpenGLImpl() override; + +public: + void restoreFramebufferBinding(); + +#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY +private: + void emergencyRecreateGL(); +#endif + + // Implementation of mbgl::gfx::RendererBackend +public: + mbgl::gfx::Renderable& getDefaultRenderable() override { + return *this; + } + +private: + void activate() override; + void deactivate() override; + // End implementation of mbgl::gfx::RendererBackend + + // Implementation of mbgl::gl::RendererBackend +public: + void updateAssumedState() override; + +private: + mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override; + // End implementation of mbgl::gl::Rendererbackend + + // Implementation of MGLMapViewImpl +public: + mbgl::gfx::RendererBackend& getRendererBackend() override { + return *this; + } + + EAGLContext* getEAGLContext() override; + void setOpaque(bool) override; + void display() override; + void setPresentsWithTransaction(bool) override; + void createView() override; + UIView* getView() override; + void deleteView() override; + UIImage* snapshot() override; + // End implementation of MGLMapViewImpl +}; diff --git a/platform/ios/src/MGLMapView+OpenGL.mm b/platform/ios/src/MGLMapView+OpenGL.mm new file mode 100644 index 0000000000..cd9489d96f --- /dev/null +++ b/platform/ios/src/MGLMapView+OpenGL.mm @@ -0,0 +1,253 @@ +#import "MGLFoundation_Private.h" +#import "MGLLoggingConfiguration_Private.h" +#import "MGLMapView+OpenGL.h" + +#include <mbgl/gl/renderable_resource.hpp> + +#import <GLKit/GLKit.h> +#import <OpenGLES/EAGL.h> +#import <QuartzCore/CAEAGLLayer.h> + +@interface MGLMapViewImplDelegate : NSObject <GLKViewDelegate> +@end + +@implementation MGLMapViewImplDelegate { + MGLMapViewOpenGLImpl* _impl; +} + +- (instancetype)initWithImpl:(MGLMapViewOpenGLImpl*)impl { + if (self = [super init]) { + _impl = impl; + } + return self; +} + +- (void)glkView:(nonnull GLKView*)view drawInRect:(CGRect)rect { + _impl->render(); +} + +@end + +class MGLMapViewOpenGLRenderableResource final : public mbgl::gl::RenderableResource { +public: + MGLMapViewOpenGLRenderableResource(MGLMapViewOpenGLImpl& backend_) + : backend(backend_), + delegate([[MGLMapViewImplDelegate alloc] initWithImpl:&backend]), + atLeastiOS_12_2_0([NSProcessInfo.processInfo + isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){ 12, 2, 0 }]) { + } + + void bind() override { + backend.restoreFramebufferBinding(); + } + + mbgl::Size framebufferSize() { + assert(glView); + return { static_cast<uint32_t>(glView.drawableWidth), + static_cast<uint32_t>(glView.drawableHeight) }; + } + +private: + MGLMapViewOpenGLImpl& backend; + +public: + MGLMapViewImplDelegate* delegate = nil; + GLKView *glView = nil; + EAGLContext *context = nil; + const bool atLeastiOS_12_2_0; + + // We count how often the context was activated/deactivated so that we can truly deactivate it + // after the activation count drops to 0. + NSUInteger activationCount = 0; +}; + +MGLMapViewOpenGLImpl::MGLMapViewOpenGLImpl(MGLMapView* nativeView_) + : MGLMapViewImpl(nativeView_), + mbgl::gl::RendererBackend(mbgl::gfx::ContextMode::Unique), + mbgl::gfx::Renderable({ 0, 0 }, std::make_unique<MGLMapViewOpenGLRenderableResource>(*this)) { +} + +MGLMapViewOpenGLImpl::~MGLMapViewOpenGLImpl() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (resource.context && [[EAGLContext currentContext] isEqual:resource.context]) { + [EAGLContext setCurrentContext:nil]; + } +} + +void MGLMapViewOpenGLImpl::setOpaque(const bool opaque) { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + resource.glView.opaque = opaque; + resource.glView.layer.opaque = opaque; +} + +void MGLMapViewOpenGLImpl::setPresentsWithTransaction(const bool value) { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + CAEAGLLayer* eaglLayer = MGL_OBJC_DYNAMIC_CAST(resource.glView.layer, CAEAGLLayer); + eaglLayer.presentsWithTransaction = value; +} + +void MGLMapViewOpenGLImpl::display() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); +#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY + // See https://github.com/mapbox/mapbox-gl-native/issues/14232 + // glClear can be blocked for 1 second. This code is an "escape hatch", + // an attempt to detect this situation and rebuild the GL views. + if (mapView.enablePresentsWithTransaction && resource.atLeastiOS_12_2_0) { + CFTimeInterval before = CACurrentMediaTime(); + [resource.glView display]; + CFTimeInterval after = CACurrentMediaTime(); + + if (after - before >= 1.0) { + dispatch_async(dispatch_get_main_queue(), ^{ + emergencyRecreateGL(); + }); + } + } + else +#endif + { + [resource.glView display]; + } +} + +void MGLMapViewOpenGLImpl::createView() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (resource.glView) { + return; + } + + if (!resource.context) { + resource.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + assert(resource.context); + } + + resource.glView = [[GLKView alloc] initWithFrame:mapView.bounds context:resource.context]; + resource.glView.delegate = resource.delegate; + resource.glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + resource.glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] + ? [[UIScreen mainScreen] nativeScale] + : [[UIScreen mainScreen] scale]; + resource.glView.contentMode = UIViewContentModeCenter; + resource.glView.drawableStencilFormat = GLKViewDrawableStencilFormat8; + resource.glView.drawableDepthFormat = GLKViewDrawableDepthFormat16; + resource.glView.opaque = mapView.opaque; + resource.glView.layer.opaque = mapView.opaque; + resource.glView.enableSetNeedsDisplay = NO; + CAEAGLLayer* eaglLayer = MGL_OBJC_DYNAMIC_CAST(resource.glView.layer, CAEAGLLayer); + eaglLayer.presentsWithTransaction = NO; + + [mapView insertSubview:resource.glView atIndex:0]; + size = resource.framebufferSize(); +} + +UIView* MGLMapViewOpenGLImpl::getView() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + return resource.glView; +} + +void MGLMapViewOpenGLImpl::deleteView() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + [resource.glView deleteDrawable]; +} + +#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY +// See https://github.com/mapbox/mapbox-gl-native/issues/14232 +void MGLMapViewOpenGLImpl::emergencyRecreateGL() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + MGLLogError(@"Rendering took too long - creating GL views"); + + CAEAGLLayer* eaglLayer = MGL_OBJC_DYNAMIC_CAST(resource.glView.layer, CAEAGLLayer); + eaglLayer.presentsWithTransaction = NO; + + [mapView pauseRendering:nil]; + + // Just performing a pauseRendering:/resumeRendering: pair isn't sufficient - in this case + // we can still get errors when calling bindDrawable. Here we completely + // recreate the GLKView + + [mapView.userLocationAnnotationView removeFromSuperview]; + [resource.glView removeFromSuperview]; + + // Recreate the view + resource.glView = nil; + createView(); + + if (mapView.annotationContainerView) { + [resource.glView insertSubview:mapView.annotationContainerView atIndex:0]; + } + + [mapView updateUserLocationAnnotationView]; + + // Do not bind...yet + + if (mapView.window) { + [mapView resumeRendering:nil]; + eaglLayer = MGL_OBJC_DYNAMIC_CAST(resource.glView.layer, CAEAGLLayer); + eaglLayer.presentsWithTransaction = mapView.enablePresentsWithTransaction; + } else { + MGLLogDebug(@"No window - skipping resumeRendering"); + } +} +#endif + +mbgl::gl::ProcAddress MGLMapViewOpenGLImpl::getExtensionFunctionPointer(const char* name) { + static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles")); + if (!framework) { + throw std::runtime_error("Failed to load OpenGL framework."); + } + + return reinterpret_cast<mbgl::gl::ProcAddress>(CFBundleGetFunctionPointerForName( + framework, (__bridge CFStringRef)[NSString stringWithUTF8String:name])); +} + +void MGLMapViewOpenGLImpl::activate() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (resource.activationCount++) { + return; + } + + [EAGLContext setCurrentContext:resource.context]; +} + +void MGLMapViewOpenGLImpl::deactivate() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (--resource.activationCount) { + return; + } + + [EAGLContext setCurrentContext:nil]; +} + +/// This function is called before we start rendering, when iOS invokes our rendering method. +/// iOS already sets the correct framebuffer and viewport for us, so we need to update the +/// context state with the anticipated values. +void MGLMapViewOpenGLImpl::updateAssumedState() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + assumeFramebufferBinding(ImplicitFramebufferBinding); + assumeViewport(0, 0, resource.framebufferSize()); +} + +void MGLMapViewOpenGLImpl::restoreFramebufferBinding() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (!implicitFramebufferBound()) { + // Something modified our state, and we need to bind the original drawable again. + // Doing this also sets the viewport to the full framebuffer. + // Note that in reality, iOS does not use the Framebuffer 0 (it's typically 1), and we + // only use this is a placeholder value. + [resource.glView bindDrawable]; + updateAssumedState(); + } else { + // Our framebuffer is still bound, but the viewport might have changed. + setViewport(0, 0, resource.framebufferSize()); + } +} + +UIImage* MGLMapViewOpenGLImpl::snapshot() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + return resource.glView.snapshot; +} + +EAGLContext* MGLMapViewOpenGLImpl::getEAGLContext() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + return resource.context; +} diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index e6323d8bc1..a304bfddfc 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1,7 +1,5 @@ #import "MGLMapView_Private.h" - -#import <GLKit/GLKit.h> -#import <OpenGLES/EAGL.h> +#import "MGLMapView+Impl.h" #include <mbgl/map/map.hpp> #include <mbgl/map/map_options.hpp> @@ -17,8 +15,6 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/style/layers/custom_layer.hpp> #include <mbgl/renderer/renderer.hpp> -#import <mbgl/gl/renderer_backend.hpp> -#import <mbgl/gl/renderable_resource.hpp> #include <mbgl/math/wrap.hpp> #include <mbgl/util/exception.hpp> #include <mbgl/util/geo.hpp> @@ -78,7 +74,6 @@ #include <map> #include <unordered_set> -class MBGLView; class MGLAnnotationContext; const MGLMapViewDecelerationRate MGLMapViewDecelerationRateNormal = UIScrollViewDecelerationRateNormal; @@ -192,15 +187,12 @@ public: #pragma mark - Private - @interface MGLMapView () <UIGestureRecognizerDelegate, - GLKViewDelegate, MGLLocationManagerDelegate, MGLSMCalloutViewDelegate, MGLCalloutViewDelegate, MGLMultiPointDelegate, MGLAnnotationImageDelegate> -@property (nonatomic, readwrite) EAGLContext *context; -@property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; @property (nonatomic) NSMutableArray<NSLayoutConstraint *> *scaleBarConstraints; @@ -275,7 +267,7 @@ public: @implementation MGLMapView { mbgl::Map *_mbglMap; - MBGLView *_mbglView; + std::unique_ptr<MGLMapViewImpl> _mbglView; std::unique_ptr<MGLRenderFrontend> _rendererFrontend; BOOL _opaque; @@ -328,9 +320,6 @@ public: CFTimeInterval _frameCounterStartTime; NSInteger _frameCount; CFTimeInterval _frameDurations; - - BOOL _atLeastiOS_12_2_0; - } #pragma mark - Setup & Teardown - @@ -441,13 +430,6 @@ public: - (void)commonInit { _opaque = NO; - _atLeastiOS_12_2_0 = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){12,2,0}]; - - BOOL background = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground; - if (!background) - { - [self createGLView]; - } // setup accessibility // @@ -463,8 +445,13 @@ public: self.preferredFramesPerSecond = MGLMapViewPreferredFramesPerSecondDefault; // setup mbgl view - _mbglView = new MBGLView(self); - + _mbglView = MGLMapViewImpl::Create(self); + + BOOL background = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground; + if (!background) + { + _mbglView->createView(); + } // Delete the pre-offline ambient cache at ~/Library/Caches/cache.db. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *fileCachePath = [paths.firstObject stringByAppendingPathComponent:@"cache.db"]; @@ -473,9 +460,9 @@ public: // setup mbgl map MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - auto renderer = std::make_unique<mbgl::Renderer>(*_mbglView, config.scaleFactor, config.cacheDir, config.localFontFamilyName); + auto renderer = std::make_unique<mbgl::Renderer>(_mbglView->getRendererBackend(), config.scaleFactor, config.cacheDir, config.localFontFamilyName); BOOL enableCrossSourceCollisions = !config.perSourceCollisions; - _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, *_mbglView); + _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, _mbglView->getRendererBackend()); mbgl::MapOptions mapOptions; mapOptions.withMapMode(mbgl::MapMode::Continuous) @@ -636,7 +623,7 @@ public: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; - // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `sleepGL:` in response to it, as doing + // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `pauseRendering:` in response to it, as doing // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; @@ -677,47 +664,6 @@ public: static_cast<uint32_t>(size.height) }; } -- (mbgl::Size)framebufferSize -{ - return { static_cast<uint32_t>(self.glView.drawableWidth), - static_cast<uint32_t>(self.glView.drawableHeight) }; -} - -+ (GLKView *)GLKViewWithFrame:(CGRect)frame context:(EAGLContext *)context opaque:(BOOL)opaque -{ - GLKView *glView = [[GLKView alloc] initWithFrame:frame context:context]; - glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - glView.enableSetNeedsDisplay = NO; - glView.drawableStencilFormat = GLKViewDrawableStencilFormat8; - glView.drawableDepthFormat = GLKViewDrawableDepthFormat16; - glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; - glView.layer.opaque = opaque; - glView.contentMode = UIViewContentModeCenter; - - return glView; -} - -- (void)createGLView -{ - if (_context) return; - - // create context - // - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - MGLAssert(_context, @"Failed to create OpenGL ES context."); - - // create GL view - // - _glView = [MGLMapView GLKViewWithFrame:self.bounds context:_context opaque:_opaque]; - _glView.delegate = self; - - CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer); - eaglLayer.presentsWithTransaction = NO; - - [_glView bindDrawable]; - [self insertSubview:_glView atIndex:0]; -} - - (UIImage *)compassImage { UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass"]; @@ -763,8 +709,7 @@ public: delete _mbglMap; _mbglMap = nullptr; - delete _mbglView; - _mbglView = nullptr; + _mbglView.reset(); _rendererFrontend.reset(); } @@ -789,11 +734,6 @@ public: [self destroyCoreObjects]; - if ([[EAGLContext currentContext] isEqual:_context]) - { - [EAGLContext setCurrentContext:nil]; - } - [self.compassViewConstraints removeAllObjects]; self.compassViewConstraints = nil; @@ -977,11 +917,13 @@ public: - (void)setOpaque:(BOOL)opaque { - _glView.layer.opaque = _opaque = opaque; + _opaque = opaque; + if (_mbglView) { + _mbglView->setOpaque(opaque); + } } -// This is the delegate of the GLKView object's display call. -- (void)glkView:(__unused GLKView *)view drawInRect:(__unused CGRect)rect +- (void)renderSync { if ( ! self.dormant && _rendererFrontend) { @@ -1138,27 +1080,7 @@ public: [self updateAnnotationViews]; [self updateCalloutView]; -#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY - // See https://github.com/mapbox/mapbox-gl-native/issues/14232 - // glClear can be blocked for 1 second. This code is an "escape hatch", - // an attempt to detect this situation and rebuild the GL views. - if (self.enablePresentsWithTransaction && _atLeastiOS_12_2_0) - { - CFTimeInterval before = CACurrentMediaTime(); - [self.glView display]; - CFTimeInterval after = CACurrentMediaTime(); - - if (after-before >= 1.0) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self emergencyRecreateGL]; - }); - } - } - else -#endif - { - [self.glView display]; - } + _mbglView->display(); } if (self.experimental_enableFrameRateMeasurement) @@ -1183,7 +1105,7 @@ public: } } -- (void)setNeedsGLDisplay +- (void)setNeedsRerender { MGLAssertIsMainThread(); @@ -1198,7 +1120,7 @@ public: { [self validateDisplayLink]; self.dormant = YES; - [self.glView deleteDrawable]; + _mbglView->deleteView(); } [self destroyCoreObjects]; @@ -1284,53 +1206,10 @@ public: // If the map is visible, change the layer property too if (self.window) { - CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer); - eaglLayer.presentsWithTransaction = _enablePresentsWithTransaction; + _mbglView->setPresentsWithTransaction(_enablePresentsWithTransaction); } } -#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY -// See https://github.com/mapbox/mapbox-gl-native/issues/14232 -- (void)emergencyRecreateGL { - MGLLogError(@"Rendering took too long - creating GL views"); - - CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer); - eaglLayer.presentsWithTransaction = NO; - - [self sleepGL:nil]; - - // Just performing a sleepGL/wakeGL pair isn't sufficient - in this case - // we can still get errors when calling bindDrawable. Here we completely - // recreate the GLKView - - [self.userLocationAnnotationView removeFromSuperview]; - [_glView removeFromSuperview]; - - _glView = [MGLMapView GLKViewWithFrame:self.bounds context:_context opaque:_opaque]; - _glView.delegate = self; - - [self insertSubview:_glView atIndex:0]; - - if (self.annotationContainerView) - { - [_glView insertSubview:self.annotationContainerView atIndex:0]; - } - - [self updateUserLocationAnnotationView]; - - // Do not bind...yet - - if (self.window) { - [self wakeGL:nil]; - CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer); - eaglLayer.presentsWithTransaction = self.enablePresentsWithTransaction; - } - else { - MGLLogDebug(@"No window - skipping wakeGL"); - } -} -#endif - - (void)willMoveToWindow:(UIWindow *)newWindow { [super willMoveToWindow:newWindow]; [self refreshSupportedInterfaceOrientationsWithWindow:newWindow]; @@ -1341,9 +1220,8 @@ public: // In iOS 12.2, CAEAGLLayer.presentsWithTransaction can cause dramatic // slow down. The exact cause of this is unknown, but this work around // appears to lessen the effects. - CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer); - eaglLayer.presentsWithTransaction = NO; - + _mbglView->setPresentsWithTransaction(NO); + // Moved from didMoveToWindow [self validateDisplayLink]; } @@ -1356,9 +1234,8 @@ public: if (self.window) { // See above comment - CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer); - eaglLayer.presentsWithTransaction = self.enablePresentsWithTransaction; - + _mbglView->setPresentsWithTransaction(self.enablePresentsWithTransaction); + [self validateDisplayLink]; } } @@ -1475,7 +1352,7 @@ public: return; } - self.lastSnapshotImage = self.glView.snapshot; + self.lastSnapshotImage = _mbglView->snapshot(); // For OpenGL this calls glFinish as recommended in // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ImplementingaMultitasking-awareOpenGLESApplication/ImplementingaMultitasking-awareOpenGLESApplication.html#//apple_ref/doc/uid/TP40008793-CH5-SW1 @@ -1488,22 +1365,26 @@ public: - (void)didEnterBackground:(NSNotification *)notification { - [self sleepGL:notification]; + [self pauseRendering:notification]; } - (void)willEnterForeground:(NSNotification *)notification { - // Do nothing, currently if wakeGL is called here it's a no-op. + // Do nothing, currently if resumeRendering is called here it's a no-op. } - (void)didBecomeActive:(NSNotification *)notification { - [self wakeGL:notification]; + [self resumeRendering:notification]; self.lastSnapshotImage = nil; } #pragma mark - GL / display link wake/sleep +- (EAGLContext *)context { + return _mbglView->getEAGLContext(); +} + - (BOOL)supportsBackgroundRendering { // If this view targets an external display, such as AirPlay or CarPlay, we @@ -1514,9 +1395,7 @@ public: return (self.window.screen != [UIScreen mainScreen]); } - - -- (void)sleepGL:(__unused NSNotification *)notification +- (void)pauseRendering:(__unused NSNotification *)notification { // If this view targets an external display, such as AirPlay or CarPlay, we // can safely continue to render OpenGL content without tripping @@ -1553,10 +1432,10 @@ public: if ( ! self.glSnapshotView) { - self.glSnapshotView = [[UIImageView alloc] initWithFrame:self.glView.frame]; - self.glSnapshotView.autoresizingMask = self.glView.autoresizingMask; + self.glSnapshotView = [[UIImageView alloc] initWithFrame: _mbglView->getView().frame]; + self.glSnapshotView.autoresizingMask = _mbglView->getView().autoresizingMask; self.glSnapshotView.contentMode = UIViewContentModeCenter; - [self insertSubview:self.glSnapshotView aboveSubview:self.glView]; + [self insertSubview:self.glSnapshotView aboveSubview:_mbglView->getView()]; } self.glSnapshotView.image = self.lastSnapshotImage; @@ -1570,11 +1449,11 @@ public: [self.glSnapshotView addSubview:snapshotTint]; } - [self.glView deleteDrawable]; + _mbglView->deleteView(); } } -- (void)wakeGL:(__unused NSNotification *)notification +- (void)resumeRendering:(__unused NSNotification *)notification { MGLLogInfo(@"Entering foreground."); MGLAssertIsMainThread(); @@ -1583,14 +1462,12 @@ public: { self.dormant = NO; - [self createGLView]; + _mbglView->createView(); self.glSnapshotView.hidden = YES; [self.glSnapshotView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; - [self.glView bindDrawable]; - _displayLink.paused = NO; [self validateLocationServices]; @@ -4286,7 +4163,7 @@ public: newAnnotationContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; newAnnotationContainerView.contentMode = UIViewContentModeCenter; [newAnnotationContainerView addSubviews:annotationViews]; - [_glView insertSubview:newAnnotationContainerView atIndex:0]; + [_mbglView->getView() insertSubview:newAnnotationContainerView atIndex:0]; self.annotationContainerView = newAnnotationContainerView; [self updatePresentsWithTransaction]; @@ -4992,7 +4869,7 @@ public: // Remember, calloutView can be nil here. [calloutView presentCalloutFromRect:calloutPositioningRect - inView:self.glView + inView:_mbglView->getView() constrainedToRect:constrainedRect animated:animateSelection]; @@ -6269,7 +6146,7 @@ public: } } -- (void)didFinishLoadingStyle { +- (void)mapViewDidFinishLoadingStyle { if (!_mbglMap) { return; @@ -6282,6 +6159,22 @@ public: } } +- (void)sourceDidChange:(MGLSource *)source { + // no-op: we only show attribution after tapping the info button, so there's no + // interactive update needed. +} + +- (void)didFailToLoadImage:(NSString *)imageName { + + if ([self.delegate respondsToSelector:@selector(mapView:didFailToLoadImage:)]) { + MGLImage *imageToLoad = [self.delegate mapView:self didFailToLoadImage:imageName]; + if (imageToLoad) { + auto image = [imageToLoad mgl_styleImageWithIdentifier:imageName]; + _mbglMap->getStyle().addImage(std::move(image)); + } + } +} + - (void)updateUserLocationAnnotationView { [self updateUserLocationAnnotationViewAnimatedWithDuration:0]; @@ -6497,7 +6390,7 @@ public: if ( ! annotationView.superview) { - [self.glView addSubview:annotationView]; + [_mbglView->getView() addSubview:annotationView]; // Prevents the view from sliding in from the origin. annotationView.center = userPoint; } @@ -6765,190 +6658,6 @@ public: return _annotationViewReuseQueueByIdentifier[identifier]; } -class MBGLView; - -class MBGLMapViewRenderable final : public mbgl::gl::RenderableResource { -public: - MBGLMapViewRenderable(MBGLView& backend_) : backend(backend_) { - } - - void bind() override; - -private: - MBGLView& backend; -}; - -class MBGLView : public mbgl::gl::RendererBackend, - public mbgl::gfx::Renderable, - public mbgl::MapObserver { -public: - MBGLView(MGLMapView* nativeView_) - : mbgl::gl::RendererBackend(mbgl::gfx::ContextMode::Unique), - mbgl::gfx::Renderable(nativeView_.framebufferSize, - std::make_unique<MBGLMapViewRenderable>(*this)), - nativeView(nativeView_) { - } - - /// This function is called before we start rendering, when iOS invokes our rendering method. - /// iOS already sets the correct framebuffer and viewport for us, so we need to update the - /// context state with the anticipated values. - void updateAssumedState() override { - assumeFramebufferBinding(ImplicitFramebufferBinding); - assumeViewport(0, 0, nativeView.framebufferSize); - } - - void restoreFramebufferBinding() { - if (!implicitFramebufferBound()) { - // Something modified our state, and we need to bind the original drawable again. - // Doing this also sets the viewport to the full framebuffer. - // Note that in reality, iOS does not use the Framebuffer 0 (it's typically 1), and we - // only use this is a placeholder value. - [nativeView.glView bindDrawable]; - updateAssumedState(); - } else { - // Our framebuffer is still bound, but the viewport might have changed. - setViewport(0, 0, nativeView.framebufferSize); - } - } - - void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override { - bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; - [nativeView cameraWillChangeAnimated:animated]; - } - - void onCameraIsChanging() override { - [nativeView cameraIsChanging]; - } - - void onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) override { - bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; - [nativeView cameraDidChangeAnimated:animated]; - } - - void onWillStartLoadingMap() override { - [nativeView mapViewWillStartLoadingMap]; - } - - void onDidFinishLoadingMap() override { - [nativeView mapViewDidFinishLoadingMap]; - } - - void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override { - NSString *description; - MGLErrorCode code; - switch (mapError) { - case mbgl::MapLoadError::StyleParseError: - code = MGLErrorCodeParseStyleFailed; - description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description"); - break; - case mbgl::MapLoadError::StyleLoadError: - code = MGLErrorCodeLoadStyleFailed; - description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); - break; - case mbgl::MapLoadError::NotFoundError: - code = MGLErrorCodeNotFound; - description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description"); - break; - default: - code = MGLErrorCodeUnknown; - description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description"); - } - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: description, - NSLocalizedFailureReasonErrorKey: @(what.c_str()), - }; - NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo]; - [nativeView mapViewDidFailLoadingMapWithError:error]; - } - - void onWillStartRenderingFrame() override { - [nativeView mapViewWillStartRenderingFrame]; - } - - void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) override { - bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; - [nativeView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered]; - } - - void onWillStartRenderingMap() override { - [nativeView mapViewWillStartRenderingMap]; - } - - void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) override { - bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; - [nativeView mapViewDidFinishRenderingMapFullyRendered:fullyRendered]; - } - - void onDidBecomeIdle() override { - [nativeView mapViewDidBecomeIdle]; - } - - void onDidFinishLoadingStyle() override { - [nativeView didFinishLoadingStyle]; - } - - void onStyleImageMissing(const std::string& imageIdentifier) override { - NSString *imageName = [NSString stringWithUTF8String:imageIdentifier.c_str()]; - - if ([nativeView.delegate respondsToSelector:@selector(mapView:didFailToLoadImage:)]) { - UIImage *imageToLoad = [nativeView.delegate mapView:nativeView didFailToLoadImage:imageName]; - - if (imageToLoad) { - auto image = [imageToLoad mgl_styleImageWithIdentifier:imageName]; - nativeView.mbglMap.getStyle().addImage(std::move(image)); - } - - } - } - - mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override { - static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles")); - if (!framework) { - throw std::runtime_error("Failed to load OpenGL framework."); - } - - CFStringRef str = - CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void* symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast<mbgl::gl::ProcAddress>(symbol); - } - - mbgl::gfx::Renderable& getDefaultRenderable() override { - return *this; - } - - void activate() override - { - if (activationCount++) - { - return; - } - - [EAGLContext setCurrentContext:nativeView.context]; - } - - void deactivate() override - { - if (--activationCount) - { - return; - } - - [EAGLContext setCurrentContext:nil]; - } - -private: - __weak MGLMapView* nativeView = nullptr; - - NSUInteger activationCount = 0; -}; - -void MBGLMapViewRenderable::bind() { - backend.restoreFramebufferBinding(); -} - @end #pragma mark - IBAdditions methods diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h index bffccec5fd..eff3f464a4 100644 --- a/platform/ios/src/MGLMapView_Private.h +++ b/platform/ios/src/MGLMapView_Private.h @@ -1,28 +1,55 @@ -#import <Mapbox/Mapbox.h> +#import "MGLMapView.h" +#import "MGLUserLocationAnnotationView.h" +#import "MGLAnnotationContainerView.h" + +#include <mbgl/util/size.hpp> namespace mbgl { class Map; class Renderer; } +@class MGLSource; + /// Minimum size of an annotation’s accessibility element. FOUNDATION_EXTERN const CGSize MGLAnnotationAccessibilityElementMinimumSize; /// Indicates that a method (that uses `mbgl::Map`) was called after app termination. -FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUnderlyingMapUnavailableException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const _Nonnull MGLUnderlyingMapUnavailableException; @interface MGLMapView (Private) /// The map view’s OpenGL rendering context. -@property (nonatomic, readonly) EAGLContext *context; +@property (nonatomic, readonly, nullable) EAGLContext *context; /// Currently shown popover representing the selected annotation. -@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation; +@property (nonatomic, nonnull) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation; + +/// Map observers +- (void)cameraWillChangeAnimated:(BOOL)animated; +- (void)cameraIsChanging; +- (void)cameraDidChangeAnimated:(BOOL)animated; +- (void)mapViewWillStartLoadingMap; +- (void)mapViewDidFinishLoadingMap; +- (void)mapViewDidFailLoadingMapWithError:(nonnull NSError *)error; +- (void)mapViewWillStartRenderingFrame; +- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered; +- (void)mapViewWillStartRenderingMap; +- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered; +- (void)mapViewDidBecomeIdle; +- (void)mapViewDidFinishLoadingStyle; +- (void)sourceDidChange:(nonnull MGLSource *)source; +- (void)didFailToLoadImage:(nonnull NSString *)imageName; /** Triggers another render pass even when it is not necessary. */ -- (void)setNeedsGLDisplay; +- (void)setNeedsRerender; + +/// Synchronously render a frame of the map. +- (void)renderSync; -- (mbgl::Renderer *)renderer; +- (nonnull mbgl::Map *)mbglMap; + +- (nonnull mbgl::Renderer *)renderer; /** Returns whether the map view is currently loading or processing any assets required to render the map */ - (BOOL)isFullyLoaded; @@ -30,4 +57,12 @@ FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUnderlyingMapUnavailableE /** Empties the in-memory tile cache. */ - (void)didReceiveMemoryWarning; +- (void)pauseRendering:(nonnull NSNotification *)notification; +- (void)resumeRendering:(nonnull NSNotification *)notification; +@property (nonatomic, nonnull) MGLUserLocationAnnotationView *userLocationAnnotationView; +@property (nonatomic, nonnull) MGLAnnotationContainerView *annotationContainerView; +@property (nonatomic, readonly) BOOL enablePresentsWithTransaction; + +- (BOOL) _opaque; + @end diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake index 0e1e6be412..0394d3880a 100644 --- a/platform/macos/config.cmake +++ b/platform/macos/config.cmake @@ -10,7 +10,7 @@ macro(mbgl_platform_core) target_add_mason_package(mbgl-core PUBLIC swiftshader) else() target_sources(mbgl-core - PRIVATE platform/darwin/src/headless_backend_cgl.cpp + PRIVATE platform/darwin/src/headless_backend_cgl.mm ) target_link_libraries(mbgl-core PUBLIC "-framework OpenGL" diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 91ed2f4cfa..d2d367d7bb 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -95,6 +95,10 @@ 556660D61E1D07E400E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */; }; 558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */; }; 558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE7A51E56161C00C7916D /* MGLFoundation.mm */; }; + 5591AC6A2298361600FF9ADF /* MGLMapView+Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5591AC682298361600FF9ADF /* MGLMapView+Impl.h */; }; + 5591AC6B2298361600FF9ADF /* MGLMapView+Impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5591AC692298361600FF9ADF /* MGLMapView+Impl.mm */; }; + 55CAF6322294407F00F17770 /* MGLMapView+OpenGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 55CAF6312294407F00F17770 /* MGLMapView+OpenGL.h */; }; + 55CAF6342294409B00F17770 /* MGLMapView+OpenGL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55CAF6332294409B00F17770 /* MGLMapView+OpenGL.mm */; }; 55CF7533213EDADF00ED86C4 /* libicu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF7532213EDADF00ED86C4 /* libicu.a */; }; 55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; }; 55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; }; @@ -416,6 +420,10 @@ 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; }; 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; }; 558DE7A51E56161C00C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; }; + 5591AC682298361600FF9ADF /* MGLMapView+Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+Impl.h"; sourceTree = "<group>"; }; + 5591AC692298361600FF9ADF /* MGLMapView+Impl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+Impl.mm"; sourceTree = "<group>"; }; + 55CAF6312294407F00F17770 /* MGLMapView+OpenGL.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+OpenGL.h"; sourceTree = "<group>"; }; + 55CAF6332294409B00F17770 /* MGLMapView+OpenGL.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+OpenGL.mm"; sourceTree = "<group>"; }; 55CF7532213EDADF00ED86C4 /* libicu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libicu.a; sourceTree = BUILT_PRODUCTS_DIR; }; 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; @@ -1262,6 +1270,10 @@ DAE6C3A01CC31E9400DB3429 /* MGLMapView.h */, DAE6C3AC1CC31EF300DB3429 /* MGLMapView_Private.h */, DAE6C3AD1CC31EF300DB3429 /* MGLMapView.mm */, + 5591AC682298361600FF9ADF /* MGLMapView+Impl.h */, + 5591AC692298361600FF9ADF /* MGLMapView+Impl.mm */, + 55CAF6332294409B00F17770 /* MGLMapView+OpenGL.mm */, + 55CAF6312294407F00F17770 /* MGLMapView+OpenGL.h */, DAE6C3A11CC31E9400DB3429 /* MGLMapView+IBAdditions.h */, DAE6C3AE1CC31EF300DB3429 /* MGLMapView+IBAdditions.mm */, DAE6C3A21CC31E9400DB3429 /* MGLMapViewDelegate.h */, @@ -1357,6 +1369,7 @@ 35C5D8491D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h in Headers */, 9250B8C32073C69100EF338C /* MGLShapeOfflineRegion.h in Headers */, 747ABE67219B2C3200523B67 /* MGLSymbolStyleLayer_Private.h in Headers */, + 55CAF6322294407F00F17770 /* MGLMapView+OpenGL.h in Headers */, DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */, DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */, DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */, @@ -1367,6 +1380,7 @@ DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */, 55335DF9212EC542000CE5F8 /* NSImage+MGLAdditions.h in Headers */, 1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */, + 5591AC6A2298361600FF9ADF /* MGLMapView+Impl.h in Headers */, 359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */, DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */, 747ABE61219B2C0000523B67 /* MGLLineStyleLayer_Private.h in Headers */, @@ -1638,6 +1652,7 @@ 1FCCEC53222EF9FE00302E3B /* MGLSDKMetricsManager.m in Sources */, DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */, DAE6C3B71CC31EF300DB3429 /* MGLMapView.mm in Sources */, + 55CAF6342294409B00F17770 /* MGLMapView+OpenGL.mm in Sources */, 40B77E461DB11BCD003DA2FE /* NSArray+MGLAdditions.mm in Sources */, DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */, 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */, @@ -1697,6 +1712,7 @@ DAD165751CF4CD7A001FF4B9 /* MGLShapeCollection.mm in Sources */, 92FC0AE6207CDD8D007B6B54 /* MGLShapeOfflineRegion.mm in Sources */, 35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */, + 5591AC6B2298361600FF9ADF /* MGLMapView+Impl.mm in Sources */, DA35A2AE1CCA091800E826B2 /* MGLCompassDirectionFormatter.m in Sources */, DACA8623201920BE00E9693A /* MGLRasterDEMSource.mm in Sources */, DA8F258C1D51CA540010E6B5 /* MGLLineStyleLayer.mm in Sources */, diff --git a/platform/macos/sdk-files.json b/platform/macos/sdk-files.json index 4448de1f5b..6bd767fb98 100644 --- a/platform/macos/sdk-files.json +++ b/platform/macos/sdk-files.json @@ -9,6 +9,7 @@ "platform/darwin/src/MGLSDKMetricsManager.m", "platform/darwin/src/NSBundle+MGLAdditions.m", "platform/macos/src/MGLMapView.mm", + "platform/macos/src/MGLMapView+OpenGL.mm", "platform/darwin/src/NSArray+MGLAdditions.mm", "platform/darwin/src/MGLOfflinePack.mm", "platform/darwin/src/NSDate+MGLAdditions.mm", @@ -68,6 +69,7 @@ "platform/darwin/src/MGLShapeCollection.mm", "platform/darwin/src/MGLShapeOfflineRegion.mm", "platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm", + "platform/macos/src/MGLMapView+Impl.mm", "platform/darwin/src/MGLCompassDirectionFormatter.m", "platform/darwin/src/MGLRasterDEMSource.mm", "platform/darwin/src/MGLLineStyleLayer.mm", @@ -177,11 +179,13 @@ "MGLNetworkConfiguration_Private.h": "platform/darwin/src/MGLNetworkConfiguration_Private.h", "NSCompoundPredicate+MGLAdditions.h": "platform/darwin/src/NSCompoundPredicate+MGLAdditions.h", "MGLSymbolStyleLayer_Private.h": "platform/darwin/src/MGLSymbolStyleLayer_Private.h", + "MGLMapView+OpenGL.h": "platform/macos/src/MGLMapView+OpenGL.h", "NSProcessInfo+MGLAdditions.h": "platform/macos/src/NSProcessInfo+MGLAdditions.h", "MGLRendererFrontend.h": "platform/darwin/src/MGLRendererFrontend.h", "NSValue+MGLStyleAttributeAdditions.h": "platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h", "NSImage+MGLAdditions.h": "platform/macos/src/NSImage+MGLAdditions.h", "MGLLight_Private.h": "platform/darwin/src/MGLLight_Private.h", + "MGLMapView+Impl.h": "platform/macos/src/MGLMapView+Impl.h", "NSCoder+MGLAdditions.h": "platform/darwin/src/NSCoder+MGLAdditions.h", "MGLOfflineStorage_Private.h": "platform/darwin/src/MGLOfflineStorage_Private.h", "MGLLineStyleLayer_Private.h": "platform/darwin/src/MGLLineStyleLayer_Private.h", diff --git a/platform/macos/src/MGLMapView+Impl.h b/platform/macos/src/MGLMapView+Impl.h new file mode 100644 index 0000000000..19583c17e5 --- /dev/null +++ b/platform/macos/src/MGLMapView+Impl.h @@ -0,0 +1,43 @@ +#import <mbgl/gfx/renderer_backend.hpp> +#import <mbgl/map/map_observer.hpp> +#import <mbgl/util/image.hpp> + +@class MGLMapView; + +typedef struct _CGLContextObject* CGLContextObj; + +class MGLMapViewImpl : public mbgl::MapObserver { +public: + static std::unique_ptr<MGLMapViewImpl> Create(MGLMapView*); + + MGLMapViewImpl(MGLMapView*); + virtual ~MGLMapViewImpl() = default; + + virtual mbgl::gfx::RendererBackend& getRendererBackend() = 0; + + // We need a static image of what was rendered for printing. + virtual mbgl::PremultipliedImage readStillImage() = 0; + + virtual CGLContextObj getCGLContextObj() { + return nullptr; + } + + // mbgl::MapObserver implementation + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) override; + void onCameraIsChanging() override; + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) override; + void onWillStartLoadingMap() override; + void onDidFinishLoadingMap() override; + void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override; + void onWillStartRenderingFrame() override; + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) override; + void onWillStartRenderingMap() override; + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) override; + void onDidFinishLoadingStyle() override; + void onSourceChanged(mbgl::style::Source& source) override; + void onDidBecomeIdle() override; + +protected: + /// Cocoa map view that this adapter bridges to. + __weak MGLMapView *mapView = nullptr; +}; diff --git a/platform/macos/src/MGLMapView+Impl.mm b/platform/macos/src/MGLMapView+Impl.mm new file mode 100644 index 0000000000..7be5545671 --- /dev/null +++ b/platform/macos/src/MGLMapView+Impl.mm @@ -0,0 +1,96 @@ +#import "MGLMapView+Impl.h" +#import "MGLMapView+OpenGL.h" +#import "MGLStyle_Private.h" +#import "NSBundle+MGLAdditions.h" + +#include <mbgl/map/map.hpp> +#include <mbgl/style/style.hpp> + +std::unique_ptr<MGLMapViewImpl> MGLMapViewImpl::Create(MGLMapView* nativeView) { + return std::make_unique<MGLMapViewOpenGLImpl>(nativeView); +} + +MGLMapViewImpl::MGLMapViewImpl(MGLMapView* nativeView_) : mapView(nativeView_) { +} + +void MGLMapViewImpl::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [mapView cameraWillChangeAnimated:animated]; +} + +void MGLMapViewImpl::onCameraIsChanging() { + [mapView cameraIsChanging]; +} + +void MGLMapViewImpl::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [mapView cameraDidChangeAnimated:animated]; +} + +void MGLMapViewImpl::onWillStartLoadingMap() { + [mapView mapViewWillStartLoadingMap]; +} + +void MGLMapViewImpl::onDidFinishLoadingMap() { + [mapView mapViewDidFinishLoadingMap]; +} + +void MGLMapViewImpl::onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) { + NSString *description; + MGLErrorCode code; + switch (mapError) { + case mbgl::MapLoadError::StyleParseError: + code = MGLErrorCodeParseStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description"); + break; + case mbgl::MapLoadError::StyleLoadError: + code = MGLErrorCodeLoadStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); + break; + case mbgl::MapLoadError::NotFoundError: + code = MGLErrorCodeNotFound; + description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description"); + break; + default: + code = MGLErrorCodeUnknown; + description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description"); + } + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: description, + NSLocalizedFailureReasonErrorKey: @(what.c_str()), + }; + NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo]; + [mapView mapViewDidFailLoadingMapWithError:error]; +} + +void MGLMapViewImpl::onWillStartRenderingFrame() { + [mapView mapViewWillStartRenderingFrame]; +} + +void MGLMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered]; +} + +void MGLMapViewImpl::onWillStartRenderingMap() { + [mapView mapViewWillStartRenderingMap]; +} + +void MGLMapViewImpl::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [mapView mapViewDidFinishRenderingMapFullyRendered:fullyRendered]; +} + +void MGLMapViewImpl::onDidBecomeIdle() { + [mapView mapViewDidBecomeIdle]; +} + +void MGLMapViewImpl::onDidFinishLoadingStyle() { + [mapView mapViewDidFinishLoadingStyle]; +} + +void MGLMapViewImpl::onSourceChanged(mbgl::style::Source& source) { + NSString *identifier = @(source.getID().c_str()); + MGLSource * nativeSource = [mapView.style sourceWithIdentifier:identifier]; + [mapView sourceDidChange:nativeSource]; +} diff --git a/platform/macos/src/MGLMapView+OpenGL.h b/platform/macos/src/MGLMapView+OpenGL.h new file mode 100644 index 0000000000..d4c6a448cd --- /dev/null +++ b/platform/macos/src/MGLMapView+OpenGL.h @@ -0,0 +1,45 @@ +#import "MGLMapView+Impl.h" +#import "MGLMapView_Private.h" + +#include <mbgl/gfx/renderable.hpp> +#include <mbgl/gl/renderer_backend.hpp> + +/// Adapter responsible for bridging calls from mbgl to MGLMapView and Cocoa. +class MGLMapViewOpenGLImpl final : public MGLMapViewImpl, + public mbgl::gl::RendererBackend, + public mbgl::gfx::Renderable { +public: + MGLMapViewOpenGLImpl(MGLMapView*); + ~MGLMapViewOpenGLImpl() override = default; + +public: + void restoreFramebufferBinding(); + + // Implementation of mbgl::gfx::RendererBackend +public: + mbgl::gfx::Renderable& getDefaultRenderable() override { + return *this; + } + +private: + void activate() override; + void deactivate() override; + // End implementation of mbgl::gfx::RendererBackend + + // Implementation of mbgl::gl::RendererBackend +public: + void updateAssumedState() override; + +private: + mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override; + // End implementation of mbgl::gl::Rendererbackend + + // Implementation of MGLMapViewImpl +public: + mbgl::gfx::RendererBackend& getRendererBackend() override { + return *this; + } + + mbgl::PremultipliedImage readStillImage() override; + CGLContextObj getCGLContextObj() override; +}; diff --git a/platform/macos/src/MGLMapView+OpenGL.mm b/platform/macos/src/MGLMapView+OpenGL.mm new file mode 100644 index 0000000000..f6168a4b80 --- /dev/null +++ b/platform/macos/src/MGLMapView+OpenGL.mm @@ -0,0 +1,89 @@ +#import "MGLMapView+OpenGL.h" +#import "MGLOpenGLLayer.h" + +#include <mbgl/gl/renderable_resource.hpp> + +#import <OpenGL/gl.h> + +class MGLMapViewOpenGLRenderableResource final : public mbgl::gl::RenderableResource { +public: + MGLMapViewOpenGLRenderableResource(MGLMapViewOpenGLImpl& backend_) : backend(backend_) { + } + + void bind() override { + backend.restoreFramebufferBinding(); + } + +private: + MGLMapViewOpenGLImpl& backend; + +public: + // The current framebuffer of the NSOpenGLLayer we are painting to. + GLint fbo = 0; + + // The reference counted count of activation calls + NSUInteger activationCount = 0; +}; + +MGLMapViewOpenGLImpl::MGLMapViewOpenGLImpl(MGLMapView* nativeView_) + : MGLMapViewImpl(nativeView_), + mbgl::gl::RendererBackend(mbgl::gfx::ContextMode::Unique), + mbgl::gfx::Renderable(mapView.framebufferSize, + std::make_unique<MGLMapViewOpenGLRenderableResource>(*this)) { + + // Install the OpenGL layer. Interface Builder’s synchronous drawing means + // we can’t display a map, so don’t even bother to have a map layer. + mapView.layer = + mapView.isTargetingInterfaceBuilder ? [CALayer layer] : [MGLOpenGLLayer layer]; +} + +mbgl::gl::ProcAddress MGLMapViewOpenGLImpl::getExtensionFunctionPointer(const char* name) { + static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (!framework) { + throw std::runtime_error("Failed to load OpenGL framework."); + } + + return reinterpret_cast<mbgl::gl::ProcAddress>(CFBundleGetFunctionPointerForName( + framework, (__bridge CFStringRef)[NSString stringWithUTF8String:name])); +} + +void MGLMapViewOpenGLImpl::activate() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (resource.activationCount++) { + return; + } + + MGLOpenGLLayer* layer = (MGLOpenGLLayer*)mapView.layer; + [layer.openGLContext makeCurrentContext]; +} + +void MGLMapViewOpenGLImpl::deactivate() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + if (--resource.activationCount) { + return; + } + + [NSOpenGLContext clearCurrentContext]; +} + +void MGLMapViewOpenGLImpl::updateAssumedState() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &resource.fbo); + assumeFramebufferBinding(resource.fbo); + assumeViewport(0, 0, mapView.framebufferSize); +} + +void MGLMapViewOpenGLImpl::restoreFramebufferBinding() { + auto& resource = getResource<MGLMapViewOpenGLRenderableResource>(); + setFramebufferBinding(resource.fbo); + setViewport(0, 0, mapView.framebufferSize); +} + +mbgl::PremultipliedImage MGLMapViewOpenGLImpl::readStillImage() { + return readFramebuffer(mapView.framebufferSize); +} + +CGLContextObj MGLMapViewOpenGLImpl::getCGLContextObj() { + MGLOpenGLLayer* layer = (MGLOpenGLLayer*)mapView.layer; + return layer.openGLContext.CGLContextObj; +} diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 77eb300aef..589a39f0b3 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -2,7 +2,6 @@ #import "MGLAttributionButton.h" #import "MGLCompassCell.h" -#import "MGLOpenGLLayer.h" #import "MGLStyle.h" #import "MGLRendererFrontend.h" #import "MGLRendererConfiguration.h" @@ -33,8 +32,6 @@ #import <mbgl/storage/reachability.h> #import <mbgl/style/image.hpp> #import <mbgl/renderer/renderer.hpp> -#import <mbgl/gl/renderer_backend.hpp> -#import <mbgl/gl/renderable_resource.hpp> #import <mbgl/storage/network_status.hpp> #import <mbgl/storage/resource_options.hpp> #import <mbgl/math/wrap.hpp> @@ -49,6 +46,7 @@ #import <unordered_map> #import <unordered_set> +#import "MGLMapView+Impl.h" #import "NSBundle+MGLAdditions.h" #import "NSDate+MGLAdditions.h" #import "NSProcessInfo+MGLAdditions.h" @@ -60,10 +58,6 @@ #import "NSPredicate+MGLPrivateAdditions.h" #import "MGLLoggingConfiguration_Private.h" -#import <QuartzCore/QuartzCore.h> -#import <OpenGL/gl.h> - -class MGLMapViewImpl; class MGLAnnotationContext; /// Distance from the edge of the view to ornament views (logo, attribution, etc.). @@ -160,7 +154,7 @@ public: @implementation MGLMapView { /// Cross-platform map view controller. mbgl::Map *_mbglMap; - MGLMapViewImpl *_mbglView; + std::unique_ptr<MGLMapViewImpl> _mbglView; std::unique_ptr<MGLRenderFrontend> _rendererFrontend; NSPanGestureRecognizer *_panGestureRecognizer; @@ -267,7 +261,7 @@ public: _isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent; // Set up cross-platform controllers and resources. - _mbglView = new MGLMapViewImpl(self); + _mbglView = MGLMapViewImpl::Create(self); // Delete the pre-offline ambient cache at // ~/Library/Caches/com.mapbox.MapboxGL/cache.db. @@ -282,9 +276,9 @@ public: MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - auto renderer = std::make_unique<mbgl::Renderer>(*_mbglView, config.scaleFactor, config.cacheDir, config.localFontFamilyName); + auto renderer = std::make_unique<mbgl::Renderer>(_mbglView->getRendererBackend(), config.scaleFactor, config.cacheDir, config.localFontFamilyName); BOOL enableCrossSourceCollisions = !config.perSourceCollisions; - _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, *_mbglView, true); + _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, _mbglView->getRendererBackend(), true); mbgl::MapOptions mapOptions; mapOptions.withMapMode(mbgl::MapMode::Continuous) @@ -300,10 +294,6 @@ public: _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, mapOptions, resourceOptions); - // Install the OpenGL layer. Interface Builder’s synchronous drawing means - // we can’t display a map, so don’t even bother to have a map layer. - self.layer = _isTargetingInterfaceBuilder ? [CALayer layer] : [MGLOpenGLLayer layer]; - // Notify map object when network reachability status changes. _reachability = [MGLReachability reachabilityForInternetConnection]; _reachability.reachableBlock = ^(MGLReachability *) { @@ -550,10 +540,7 @@ public: delete _mbglMap; _mbglMap = nullptr; } - if (_mbglView) { - delete _mbglView; - _mbglView = nullptr; - } + _mbglView.reset(); } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(__unused NSDictionary *)change context:(void *)context { @@ -718,8 +705,7 @@ public: } - (CGLContextObj)context { - MGLOpenGLLayer *layer = _isTargetingInterfaceBuilder ? nil : (MGLOpenGLLayer *)self.layer; - return layer.openGLContext.CGLContextObj; + return _mbglView->getCGLContextObj(); } - (void)setFrame:(NSRect)frame { @@ -820,7 +806,11 @@ public: } } -- (void)setNeedsGLDisplay { +- (BOOL)isTargetingInterfaceBuilder { + return _isTargetingInterfaceBuilder; +} + +- (void)setNeedsRerender { MGLAssertIsMainThread(); [self.layer setNeedsDisplay]; @@ -987,7 +977,7 @@ public: - (void)print:(__unused id)sender { _isPrinting = YES; - [self setNeedsGLDisplay]; + [self setNeedsRerender]; } - (void)printWithImage:(NSImage *)image { @@ -3038,175 +3028,4 @@ public: _mbglMap->setDebug(options); } -class MGLMapViewImpl; - -class MGLMapViewRenderable final : public mbgl::gl::RenderableResource { -public: - MGLMapViewRenderable(MGLMapViewImpl& backend_) : backend(backend_) { - } - - void bind() override; - -private: - MGLMapViewImpl& backend; -}; - -/// Adapter responsible for bridging calls from mbgl to MGLMapView and Cocoa. -class MGLMapViewImpl : public mbgl::gl::RendererBackend, - public mbgl::gfx::Renderable, - public mbgl::MapObserver { -public: - MGLMapViewImpl(MGLMapView* nativeView_) - : mbgl::gl::RendererBackend(mbgl::gfx::ContextMode::Unique), - mbgl::gfx::Renderable(nativeView_.framebufferSize, - std::make_unique<MGLMapViewRenderable>(*this)), - nativeView(nativeView_) { - } - - void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override { - bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; - [nativeView cameraWillChangeAnimated:animated]; - } - - void onCameraIsChanging() override { - [nativeView cameraIsChanging]; - } - - void onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) override { - bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; - [nativeView cameraDidChangeAnimated:animated]; - } - - void onWillStartLoadingMap() override { - [nativeView mapViewWillStartLoadingMap]; - } - - void onDidFinishLoadingMap() override { - [nativeView mapViewDidFinishLoadingMap]; - } - - void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override { - NSString *description; - MGLErrorCode code; - switch (mapError) { - case mbgl::MapLoadError::StyleParseError: - code = MGLErrorCodeParseStyleFailed; - description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description"); - break; - case mbgl::MapLoadError::StyleLoadError: - code = MGLErrorCodeLoadStyleFailed; - description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); - break; - case mbgl::MapLoadError::NotFoundError: - code = MGLErrorCodeNotFound; - description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description"); - break; - default: - code = MGLErrorCodeUnknown; - description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description"); - } - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: description, - NSLocalizedFailureReasonErrorKey: @(what.c_str()), - }; - NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo]; - [nativeView mapViewDidFailLoadingMapWithError:error]; - } - - void onWillStartRenderingFrame() override { - [nativeView mapViewWillStartRenderingFrame]; - } - - void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) override { - bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; - [nativeView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered]; - } - - void onWillStartRenderingMap() override { - [nativeView mapViewWillStartRenderingMap]; - } - - void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) override { - bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; - [nativeView mapViewDidFinishRenderingMapFullyRendered:fullyRendered]; - } - - void onDidBecomeIdle() override { - [nativeView mapViewDidBecomeIdle]; - } - - void onDidFinishLoadingStyle() override { - [nativeView mapViewDidFinishLoadingStyle]; - } - - void onSourceChanged(mbgl::style::Source& source) override { - NSString *identifier = @(source.getID().c_str()); - MGLSource * nativeSource = [nativeView.style sourceWithIdentifier:identifier]; - [nativeView sourceDidChange:nativeSource]; - } - - mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override { - static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - if (!framework) { - throw std::runtime_error("Failed to load OpenGL framework."); - } - - CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void *symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast<mbgl::gl::ProcAddress>(symbol); - } - - mbgl::gfx::Renderable& getDefaultRenderable() override { - return *this; - } - - void activate() override { - if (activationCount++) { - return; - } - - MGLOpenGLLayer *layer = (MGLOpenGLLayer *)nativeView.layer; - [layer.openGLContext makeCurrentContext]; - } - - void deactivate() override { - if (--activationCount) { - return; - } - - [NSOpenGLContext clearCurrentContext]; - } - - void updateAssumedState() override { - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); - assumeFramebufferBinding(fbo); - assumeViewport(0, 0, nativeView.framebufferSize); - } - - void restoreFramebufferBinding() { - setFramebufferBinding(fbo); - setViewport(0, 0, nativeView.framebufferSize); - } - - mbgl::PremultipliedImage readStillImage() { - return readFramebuffer(nativeView.framebufferSize); - } - -private: - /// Cocoa map view that this adapter bridges to. - __weak MGLMapView *nativeView = nullptr; - - /// The current framebuffer of the NSOpenGLLayer we are painting to. - GLint fbo = 0; - - /// The reference counted count of activation calls - NSUInteger activationCount = 0; -}; - -void MGLMapViewRenderable::bind() { - backend.restoreFramebufferBinding(); -} - @end diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h index c7d6786666..1de4b47eb7 100644 --- a/platform/macos/src/MGLMapViewDelegate.h +++ b/platform/macos/src/MGLMapViewDelegate.h @@ -181,6 +181,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style; +- (nullable NSImage *)mapView:(MGLMapView *)mapView didFailToLoadImage:(NSString *)imageName; + #pragma mark Managing the Appearance of Annotations /** diff --git a/platform/macos/src/MGLMapView_Private.h b/platform/macos/src/MGLMapView_Private.h index 986ae1143a..afd7cf2422 100644 --- a/platform/macos/src/MGLMapView_Private.h +++ b/platform/macos/src/MGLMapView_Private.h @@ -1,10 +1,14 @@ #import "MGLMapView.h" +#include <mbgl/util/size.hpp> + namespace mbgl { class Map; class Renderer; } +@class MGLSource; + @interface MGLMapView (Private) /// True if the view or application is in a state where it is not expected to be @@ -22,17 +26,36 @@ namespace mbgl { /// Center longitude set independently of the center latitude in an inspectable. @property (nonatomic) CLLocationDegrees pendingLongitude; -/// The map view’s OpenGL rendering context. -- (CGLContextObj)context; +/// The map view’s OpenGL rendering context, if it is backed by an OpenGL based view. +@property (readonly, nonatomic, nullable) CGLContextObj context; + +- (mbgl::Size)framebufferSize; + +/// Map observers +- (void)cameraWillChangeAnimated:(BOOL)animated; +- (void)cameraIsChanging; +- (void)cameraDidChangeAnimated:(BOOL)animated; +- (void)mapViewWillStartLoadingMap; +- (void)mapViewDidFinishLoadingMap; +- (void)mapViewDidFailLoadingMapWithError:(nonnull NSError *)error; +- (void)mapViewWillStartRenderingFrame; +- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered; +- (void)mapViewWillStartRenderingMap; +- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered; +- (void)mapViewDidBecomeIdle; +- (void)mapViewDidFinishLoadingStyle; +- (void)sourceDidChange:(nonnull MGLSource *)source; /// Asynchronously render a frame of the map. -- (void)setNeedsGLDisplay; +- (void)setNeedsRerender; /// Synchronously render a frame of the map. - (void)renderSync; -- (mbgl::Map *)mbglMap; +- (BOOL)isTargetingInterfaceBuilder; + +- (nonnull mbgl::Map *)mbglMap; -- (mbgl::Renderer *)renderer; +- (nonnull mbgl::Renderer *)renderer; @end diff --git a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift index 83c7160fde..6c37017be6 100644 --- a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift +++ b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift @@ -28,6 +28,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate { func mapViewDidBecomeIdle(_ mapView: MGLMapView) {} func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {} + + func mapView(_ mapView: MGLMapView, didFailToLoadImage imageName: String) -> NSImage? { return nil } func mapView(_ mapView: MGLMapView, shapeAnnotationIsEnabled annotation: MGLShape) -> Bool { return false } |