Mac NSView animation animationDidEnd called twice on Retina MBP - macos

A bug recently showed up in my app related to view animation on the new MBP Retina. I don't have a new MPB to reproduce but the affected user is helping track down the issue through copious amounts of debugging output. It appears that animationDidEnd is being called twice on my animation delegate, the second time seems to be screwing things up immensely. The code has worked on 10.5-10.7.4 for quite some time now and this seems to be isolated to the new MBP Retina so far.
I am using the view itself as the animation delegate in case something about the relationship between the view and the animation delegate has changed which precludes this possibility. I'm also further investigating the possibility of the animationDidEnd method being called by two distinct animation objects (though I have nothing to indicate that another animation is running anywhere in the app, let alone for this delegate).
If anyone is aware of any updates to documentation related to animation delegates I would appreciate a pointer, or any ideas otherwise. Thanks.

SOLVED: The issue didn't have to do with animations at all. It had to do with use of the deprecated method convertPointFromBase:
While deprecated methods are "usually" ok for at least the next release, this one is trouble when it comes to the Retina display. This is only conjecture, but since the method works as expected on non-Retina displays, I have to assume this has to do with the pixel density on the new displays.

Related

SCNView overlay causes tearing on resize

I'm using SceneKit to display a 3D scene (so far, a single quad), and the overlaySKScene to display a 2D overlay (which so far is just a SKNode with no geometry, though I had previously used a single SKLabelNode). It's a pretty simple view inside a bunch of nested NSSplitView. And during normal use, it works brilliantly. The problem comes when I try to resize the window or split view — I get areas of red leaking through my nice background, which disappear shortly after.
I'm running this on a 2016 MBP with a Radeon Pro 460, and captured this frame using Quicktime's screen capture:
Disabling the overlay removes the red areas, which makes me think that it's the problem. Disabling the statistics bar or the scroller (a child view of the SCNView) do not have any impact. My most minimal SKScene subclass is defined as
#implementation TestOverlay
- (instancetype) initWithSize: (CGSize) size
{
if( self = [super initWithSize: size] )
{
// Setup the default state
self.scaleMode = SKSceneScaleModeResizeFill;
self.userInteractionEnabled = NO;
self.backgroundColor = [NSColor blackColor];
}
return self;
}
#end
Has anybody run into similar issues before? Annoyingly, the apple sample Fox2 doesn't have similar problems...
For true enlightenment, one needs to read the documentation carefully, then comment everything out and restore functionality one step at a time. And then read the documentation again.
In the discussion section of -[SCNSceneRendererDelegate renderer:willRenderScene:atTime:], the solution is obvious (emphasis mine):
You should only execute Metal or OpenGL drawing commands (and any setup required to perform them) in this method—the results of modifying SceneKit objects during this method are undefined.
Which is exactly what I was doing. I had misread this as modifying geometry, so thought that assigning textures would be reasonable to do here (after all, "will" render means it hadn't started rendering yet, right?), and would therefore pick the most recently created texture. And unfortunately, before I decided that I needed an overlay, this actually works perfectly well! As soon as the overlay was added, however, the tearing appeared.
The correct place to update material properties seems to be -[SCNSceneRendererDelegate renderer:updateAtTime]. Use that to avoid silly bugs like this one folks!
Try to reset SMC (System Management Controller). It helped me for solving a similar problem but with Autodesk Maya 2018 on MBP 2017 (Radeon 560).
So, shut down and unplug your MBP.
On the built-in keyboard, press and hold the Shift-Option-Control keys on the left side and press the Power Button and hold all of these down for 10 seconds, then release the keys.
Connect the power adapter and then turn the Mac on normally.
Hope this helps.
P.S. In case it doesn't help, try to check/uncheck Automatic graphics switching option in System Preferences–Energy Saver to see if there's a difference.

Frame-by-frame animation in MacOs

I'm looking for a way to do frame-by-frame programmatically drawn animations in a MacOs application (not keyframe property animation). I have tried drawing to CALayers using the drawLayer:inContext: delegate method, calling setNeedsDisplay to draw each frame, however I'm getting poor performance doing it this way. Is there a recommended way to do this type of animation in Cocoa?
A good way to do entirely custom animations is by using CADisplayLink (iOS) or CVDisplayLink (macOS). CVDisplayLink is basically a timer that fires as often as the display refreshes.
You can then calculate your own timing functions based on the values you get off CVDisplayLink. The API is still C so it is a bit cumbersome to use, especially in Swift, but once you get how it functions it works like a charm.
I have only had good experiences with CVDisplayLink, especially with layers. They are really performant. I was able to animate 1000+ layers CVDisplayLink driven at 60fps without any problems.
If you need any help in using the API, feel free to ask!
Alternative:
If you want to use a more modern API, I can recommend SpriteKit. There are some nice animation APIs as well. And they perform really good. Apple uses it itself to draw more complex views (like the Memory Debugger in Xcode).

SKSceneScaleModeResizeFill on ios8 scales improperly

I have written a game in SpriteKit using objective C and it works perfectly on ios9 but it looks hideous on ios8. I would really like to know how to fix this problem, either by “correcting” my mistake, or if I have no mistake then by finding a workaround for the bug in ios8.
I think I have really done all I can to make the problem as clear as possible, including making loads of screenshots to illustrate the problem and also making a new Xcode project that is as simple as possible while still showing the problem.
If you want to try the Xcode project, here is a link for it….
xcode project
If you want to see the screenshots of the problem, then here is a link for the screenshots.
Screenshots
Now I will try to explain the code I wrote and the problem illustrated in the screenshots.
PLEASE REMEMBER: My code works perfectly on iOS9.3. So my code is obviously not complete gargage. But admittedly, I am not an expert on handling screen rotation, so probably my code could be better.
I should probably mention that both scenes have scale mode set to SKSceneScaleModeResizeFill. I chose this mode because I had tremendous difficulty doing proper layouts for all possible screen sizes (including iPhone) when working with SKSceneScaleModeAspectFill. I do I hope I can solve this problem while sticking with SKSceneScaleModeResizeFill.
Anyway, my app is a SpriteKit game with two scenes. The main scene is the GameScene, where you play the game. And this scene has a pointer to the SettingsScene, where you can change the settings of the scene. (e.g. change the level of difficulty).
Anytime the user rotates the screen, GameViewController detects this change in viewWillTransitionToSize and tells the GameScene object about the new screen width and screen height. Game Scene then adjusts the positions of its sprites in consideration of the new screen orientation and then tells its SettingsScene object about the new screen width and height to that the Settings scene is properly laid out as well.
Please note that with this design, all sprites on BOTH scenes get repositioned any time the user rotates the screen REGARDLESS of which scene is actually active at that time..
As I said before, all works as expected on ios9.3. But on ios8, the result is attrocious. The screenshots illustrate one example of typical experience on ios8. If the user rotates the screen while using the game and then goes to the settings screen, he will see something awful. And will often be trapped in this terrible experience because the button for going back to the main game might not even be fitting on the screen anymore.
At first, it might seem like I am failing to reposition sprites for landscape mode in the settings scene. But this explanation is wrong. The text on the screen shows that the last layout was performed with the landscape orientation in mind.
So what is going wrong here?
Any suggestions would be highly highly highly highly appreciated.
Thanks!
-j
p.s. In case you don't want to look directly at the linked project file, here are some details about the example code. GameViewController implements viewTransitionToSize to handle any screen rotation. It directly tells the new screen dimensions to GameScene, which then tells SettingsScene. Both scenes rearrange their sprites in consideration of the new screen dimensions. And all goes well on ios9. On ios8, however, the inactive scene ends up looking hideaous when it is presented even though it clearly did reposition its sprites according to the new dimensions.
the problem is easily resolved by these lines....
gameScene.size = newScreenSize;
settingsScene.size = newScreenSize;
anytime the orientation changes.
This code is not required for ios9. The scene knows what size the screen is without assistance. But for ios8, it seems to be needed to add this code.

Forcing OSX NSScroller thumb to draw in highlighted form without actually mouse tracking

I'm drawing an NSScroller into a bitmap. I need to capture it with the thumb highlighted (I'm using cacheDisplayInRect:toBitmapImageRep:, but I've tried the separate draw methods into a GC created on the bitmap). I've tried everything I can think of, including setting various values in the (private) _sFlags2 and sFlags NSScroller ivars before the draw call. I can't send it events because the scroller isn't actually live.
Eventually I need this to work on 10.6+, but all of my testing so far has been on 10.7+ (which is where the new style scrollbars came in), and I haven't checked 10.6 yet because I'm also using alignmentRectForFrame: and haven't faked that out for 10.6 yet.
After some quality time spent with Hopper! I found the following solution:
[[scrollbar scrollerImp] setShouldDrawRolloverState: YES];
Undocumented, but I'm a happy camper!

CALayer flickering when adding a foreground layer to IKImageBrowserView items with garbage collection on

I'm trying to implement a technique similar to the one in the ImageBrowserViewAppearance sample code from Apple (located here: http://developer.apple.com/library/mac/#samplecode/ImageBrowserViewAppearance/Introduction/Intro.html ), where CALayers are generated on top of the items in the IKImageBrowserView to customize the appearances of the objects in the image browser.
However, I'm getting a weird problem when I turn on garbage collection, and I can reproduce it in the Apple sample code. Simply turn on Garbage Collection in the target, and build and launch the ImageBrowserAppearance sample app. Then, add some photos to the image browser using the "Add Photos..." button.
Now, click on an empty portion of the IKImageBrowserView, and click and drag to start selecting multiple items in the browser view. As you drag the selection box around, you should notice that sometimes the pin and gloss overlay for some of the items flicker and briefly appear in the bottom-left corner of the IKImageBrowserView. All of the CALayers seem to do this occasionally, I've seen the white surrounding slide area flicker down into the bottom-left corner as well.
When I mimic the technique in my own code, I (not surprisingly) also can reproduce this badge flickering. However, this problem disappears when garbage collection is off.
Anybody have a clue what could be going wrong here? I'd like to use garbage collection in my app in conjunction with this technique, but the flickering is kind of annoying.
I bookmarked this a while back but Apple's changed the URL and the text. Fortunately I quoted it when I bookmarked it:
The Core Graphics APIs (Quartz 2D) see an approximately 25% reduction in drawing performance for applications compiled to use garbage collection.
That "25% reduction in drawing performance" text has been rewritten into a "slight overhead in code execution" and that was for 10.5. Perhaps Apple fixed it for 10.6. And you're talking Core Animation, not Core Graphics.
Still, Core Animation eventually has to talk to Core Graphics, and perhaps that performance issue hasn't gone away, and you're being bitten by it.
I fooled around with this a bit and can confirm I get the same behavior running the project with GC turned on. In fact, if you're patient enough and slowly change the selection one image at a time using the arrow keys, eventually it'll trigger the behavior and you can see the layers from one image in the view are displayed in the lower left corner instead of on top of the image. I haven't been able to find any sort of pattern as to when it happens, or any relation between which image is selected and which image has its layers missing. I'm assuming that for whatever reason, those layers are getting their frame origin set to {0, 0}, but heck if I know why.

Resources