I have a MapKit issue with MKMapView using addOverlay and rendererForOverlay. Testing and debugging is being done on a device (iPhone 7 iOS 11.1.1) with Xcode 9.1 (9B55). The overlay renderer is being refreshed repeatedly for all tiles in the map view (2500 calls per sec to drawMapRect:). The calls to the renderer are ignoring the changed rectangle in setNeedsDisplayInMapRect: and are not initiated by setNeedsDisplayInMapRect. This refreshing continues forever even after all map updates were finished with Xcode reporting the app is using over 160% CPU.
Xcode Debug Navigator Image Link
The MKMapView code is based on the Apple Sample code 'BreadCrumb' available from https://developer.apple.com/library/content/samplecode/Breadcrumb/Introduction/Intro.html. There are no significant structural changes to this code.
Has anyone else experienced this or have any suggestions of where to start looking for a solution?
Running the Apple Breadcrumb sample did not exhibit the same problem. After putting this back into my project and adding the changes from my project I was finally able to isolate the problem to having inserted 'self.alpha = 0.5' into drawMapRect:. It does not matter whether the alpha property is set to 1.0 or some other value, the problem will still occur.
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)context;
{
CrumbPath *crumbs = (CrumbPath *)(self.overlay);
self.alpha = 0.5; // <-------- THE PROBLEM
With the problem resolved overlay renderer calls reverted to between 40 and 80 per second with no calls occurring without map updates and calls to setNeedsDisplayInMapRect:.
Related
I noticed a performance issue when using GMSMapView as part of my view hierarchy. Important note: the map doesn't take up the whole screen, it is used as a table view header. These issues affect the behaviour of the table view itself - low FPS, which results in a bad user experience, so I'm trying to resolve these issues or at least understand what GMSMapView is doing.
Using Time Profiler I found out that this is cased by the GMSMapView re-rendering on every frame (as I can tell), because the heaviest stack trace on the main thread is:
Which is called from:
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
(I guess it's just how internals of Google Maps work - it registers a CADisplayLink with a runloop and re-renders it on each frame)
This results in a heavy CPU usage (up to 99%):
Note that on the selected area nothing really happens to the app, it just displays a static map and a table view (no user interaction, no frame changes, no anything)
If I conduct the same test without a GMSMapView as a subview, the CPU usage in the same place is almost 0%:
Now to the question. Why is that happening and how to stop this behaviour?
I found a method called - (void) stopRendering on a GMSMapView, and tested it - the results are good and I get the same performance as when I completely remove the map view. However, this method is marked as deprecated and it states that it will be removed in a future releases of the SDK, which makes it a bad candidate for a long-term solution.
Any help, explanation or clues will be appreciated!
The problem with stopRendering is that it places the burden of managing the map state on the developer.
Instead of using stopRendering, you should limit the frame rate of the object. GMSMapView has a property called preferredFrameRate. You should be using that. It says in the documentation that by default preferredFrameRate is set to maximum, or to re-render every frame.
preferredFrameRate is of an enum called GMSFrameRate. Which has values:
kGMSFrameRatePowerSave
kGMSFrameRateConservative
kGMSFrameRateMaximum
You should be using #2 to ensure that the map stays fluid during user interaction, but also doesnt render needlessly. By default preferredFrameRate is set to #3.
Since upgrading to Xcode 8.1 my UI tests that use twoFingerTap() to zoom out on a Google Maps GMSMapView have been failing with
Assertion Failure: Element.swift:135: UI Testing Failure - Unable to find unoccluded area to perform event.
The message preceding that is
Recompute visible frame by excluding frames of occluding elements StatusBar and "the view's identifier"
Anyone have any idea what I should do about that? Tried tapWithNumberOfTaps(1, numberOfTouches: 2) and same thing happens.
NB. The problem is definitely with multi-finger taps only -- doubleTap() and pinchWithScale(2.0, velocity: 1.0) continue to work fine under Xcode 8.1. pinchWithScale(0.5, velocity: -1.0) continues to move origin instead of zooming, which is what it did to GMSMapView in Xcode 7 as well.
I am not sure if you're drawing your UI, or using storyboards. Nevertheless, it doesn't appear that you have the elements drawn to the view properly. Perhaps using constraints you can secure the element to your view.
Ive just upgraded to Xcode9 (beta) and this issue appears to be resolved
Not sure if you already solve your case but It will help if you put Google Map inside another view and then set it's accessibility in storyboard (make sure Accessibility's "Enabled" and "User Interaction Enabled" is checked)
Then you can find it inside otherElements and tap or doubleFingerTap on it.
After upgrading Nexus 5 to Android 5.0, an activity with default focus on an EditText does not render correctly (EditText repeats down across the screen with grey dots in between and if you click again or dump the ViewHierarchy with UiAutomator, it will return to normal rendering).
(I would upload the image, but don't yet have reputation for images).
NOTE: This is ONLY after I have loaded a WebView within the application (though in a separate activity). The same screen renders correctly prior to loading the first WebView in the application.
NOTE: This is ONLY a problem on Android 5.0 and (so far) on Nexus 5. I do not have another 5.0 (non-nexus) device to try.
EDIT: This also happened on HTC One with Android 5.01.
NOTE: I have tried disabling hardware acceleration, modifying inputMode, and defaultFocus.
Has anyone seen or solved this problem?
Ended up solving this by changing softInputMode (similar to previous WebView/Keyboard issues, but this time with a native View and EditText).
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
I had a similar issue on Lollipop 5.0.1 devices. My solution was to deactivate hardware acceleration in the WebView on these devices before loading any content with loadURL or loadData.
int SDKversion = android.os.Build.VERSION.SDK_INT;
if(SDKversion >= android.os.Build.VERSION_CODES.LOLLIPOP){
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
If you do not want to do this, I could also drastically reduce the problem by adding a visibility="gone" webview to the disturbed activities and then calling something like:
webview_dummy.loadData("<head></head><body></body>", "text/html", "utf8");
after the loadURL of the actual webview.
Man, this is really some weird bug!
What solved my problem was to disable the hardware acceleration only on the activity which hosted my fragment. Not on the whole app, but specifically on that activity.
The app uses OpenGL ES2 and the GLKit framework, and the render/update loop provided by GLKitViewController. It used to run at a steady 60 fps on my iPad2 with iOS7.1, but once I updated the iPad2 to iOS8.1, the exact same code now fluctuates between 56-59 FPS. (CPU utlitization, however, remains at 40-60% as before ).
Profiling reveals that the OpenGL drawing commands are using a much larger proportion of CPU time than they used to. The biggest change seems to be that calls to "GLKBaseEffect prepareToDraw" are taking much longer than they used to.
(The app uses a single GLKBaseEffect which is reconfigured at various points during the render loop, neccessitating a call to prepareToDraw each time. I realise it may be possible to optimize by having multiple instances of GLKBaseEffect, and that is something I was considering for later, however, the performance, as it was, was solid on iOS7.1)
I'm now examining theĀ OpenGL ES Analyzer trace in Instruments to determine the OpenGL calls generated by "GLKBaseEffect prepareToDraw", to see if anything seems unusual, and will update the post accordingly once I've managed to figure anything out.
I'd be very grateful for any guidance on how to progress at this point - why might calls to GLKBaseEffect prepareToDraw take longer on iOS8.1?
The cause of the problem was identified by Jim Hillhouse and confirmed by Frogblast on the Apple Dev Forums thread "OpenGL Performance Drops > 50% in iOS 8 GM": setting the text property of a UITextField (or UILabel, in my case) in a view which is a subview of GLKView is causing the GLKView superview to layout, which is then causing framebuffers to be deallocated and reallocated. This wasn't happening in iOS 7.
Jim Hillhouse's workaround was to place the subview inside a UIViewController, and embed that in GLKView. I've done the same, using a Container View to hold the view controller, and can confirm that it works.
My iOS application stops rendering in case if the GLKView drawableMultisample is GLKViewDrawableMultisample4X. Everything works fine with the GLKViewDrawableMultisampleNone but if I set it to GLKViewDrawableMultisample4X, so I get only blank pink screen.
I've checked it on the iOS Simulator / iOS 7.0.3
Is anybody know how to resolve this issue ? Is it may be related to the iOS simulator and may work good on the real device?
I was having exactly this issue. It's not clear if the cause is the same but, in my case I was triggering my render from a displayLink trigger - without any regard for the semantics of setNeedsDisplay, or how GLKit sets up the render buffer around the execution of the drawInRect method.
I was of the mindset that since I was using displayLink, I could run all my rendering directly off of that trigger - and since it all worked before I tried to set up anti-aliasing, i figured it couldn't be far wrong!
The problem only manifested when I set GLKViewDrawableMultisample4X, much like the OP's problem.
The solution...
Ensure the view is created with enableSetNeedsDisplay = NO
Have displayLink trigger a function that contains nothing more than the following:
- (void)render:(CADisplayLink*)displayLink {
// This function should *not* perform any rendering
// We only want to inform GLKit that we're ready to render
GLKView * view = [self.window.subviews objectAtIndex:0];
// Tell GLKit that we're ready to draw
[view display];
// GLKit will ensure the buffers are setup before
// calling drawInRect
}
Move all rendering into drawInRect. GLKit will ensure the buffers are setup before drawInRect is called.