Using XCode 5.1 with custom OpenGL view.
I have a separate thread that processes data and calls setNeedsDisplay to refresh the display.
This is working well, but sometimes after running for a while and using other apps, when I return to my app, drawRect of the OpenGL view is stopped being called.
I see that setNeedsDisplay is being called and, drawRect is called whenever I resize the window.
Can anyone suggest a solution or at least a way to track down the reason for the sudden change in this behavior?
To whoever might be interested:
Seems like calling setNeedsDisplay from another thread might cause problems, together with a high frequency of these calls.
I've significantly lowered the amount of calls to setNeedsDisplay and changed the calls to performSelectorOnMainThread and it seems to solve the probem.
Related
Suppose you have a scrollable NSView. When you scroll it, the part coming into the clipping rectangle will need to be refreshed, and the view's drawRect (Swift draw) method will be called for a rectangle containing that new part. Suppose now that before that part can be drawn, some time-consuming processing will need to be done. Since you don't want to burden the main thread (from which drawRect is called) with that processing, you might want to unload it on a secondary thread. Now my question is, what's a good way to do this? For example, you can:
Initiate asynchronous processing from drawRect, exit it without drawing, and have the secondary thread, when it's finished, store the data somewhere and call setNeedsDisplay and thus have drawRect get called again.
As above, but when the processing is finished, draw into the view directly (in the main thread), without going through drawRect.
Set canDrawConcurrently for the view beforehand; but that, as far as I can tell, will not necessarily force drawRect into a secondary thread, unless the system decides to do so.
Or, maybe some other way? This seems like a common enough situation, but I can't find any tips on how to do this in a clean way.
I have a strange UI behavior which made me think that this API might be asynchronous. Assuming it is (async), I've made some changes and now the UI works as expected. I didn't find the answer in Apple's documentation.
Is anyone familiar or had some experience with this? Is it really async? Thanks!
EDIT: All UI changes are being made from the main thread.
setFrame:display: updates the frame and tells the system to redraw the window (and possibly the subviews) during its next drawing cycle. Cocoa drawing is always done during its drawing cycle, not at the moment you call a drawing method. See What is the most robust way to force a UIView to redraw? for some more discussion on that.
This is better thought of as drawing being "consolidated" rather than "asynchronous." Unless there are animations, all drawing should generally be complete before the next event loop. It's possible that a view will somehow delay its drawing to a later event loop, but rare. Layer-backed views in 10.8+ can support actual async drawing, but this is not the default.
From you description, it's possible you're calling setFrame:display: from a thread other than the main thread. AppKit drawing is not thread safe. You cannot call drawing methods from anywhere but the main thread, or you'll get "strange UI behavior." (This isn't completely true. On OS X, you can use lockFocus to draw from other threads, but they still won't actually be applied until the next drawing cycle after you unlock focus. But normally it's best just to draw from the main thread in modern OS X since GCD makes this simple.)
Working on https://github.com/madebybright/Nimble/
Trying to change the view frame size when the query function is run, but I keep getting
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.
when the query function runs.
When assigning the size in viewDidLoad everything works fine, but in query (https://github.com/madebybright/Nimble/blob/unstable/Nimble/MenuViewController.swift#L43) the error is thrown.
Any idea why?
Yes. It's because
This application is modifying the autolayout engine from a background thread.
:)
To be more specific, you must do all UI-related work on the main thread. You can use dispatch_async to enqueue a task (such as your frame-size adjustment) on the main thread.
I am creating an application which subclasses NSOpenGLView to do some OpenGL drawings. Now i wanted to add an overlay control, similar to QuickTime X. First thing i tried was setting NSOpenGLCPSurfaceOrder to -1 and making my window none-opaque. It did work, but i lost the windows shadow, so not a viable solution.
I then made my NSOpenGLView subclass layer-backed by calling [setWantsLayer:YES] and adding my control box as a subview. It worked, but i noticed a big drop in performance. I did some research and found this:
I am using an NSTimer object to call a method [timerFired:] 60 times per second. Works perfectly. The only thing i do within this method is calling [self setNeedsDisplay:YES], because when using a layer-backed OpenGLView one needs to overload the [drawRect:rect] method and do all the OpenGL drawing there. The problem: [drawRect:rect] often gets called although the timer didn't fire.
At first i thought "of course it does, it draws the NSOpenGLView, so it might be called by the window manager or something". So deleted my timer object to determine how it was called between [timerFired:] calls. The result: It wasn't called at all, at least not when not resizing or dragging the window.
So next i experimented with my timers time interval. Turns out, up until 55 times per second the [drawRect:rect] is called between 60 and 70 times per second, and from a timer interval of 59 times a second onward the [drawRect:rect] is called between 100 and 120 times a second.
I suspect that this highly unpredictable manner in which my drawing is called leads to the performance loss, either by being uneven or by clogging the thread with a lot of OpenGL drawings and not enough time to be executed or something. I also read that layer-backed OpenGLViews don't work with vsync, although i can't confirm this.
Does anyone have an explanation? Does anyone have an idea on how to only draw my OpenGL on timer fires?
I already tried the naive approach and added a boolean variable, set it true in my [timerFired:] and made my [drawRect:rect] only calling the OpenGL drawing method if the variable is true. The result was an extremely flickery and stuttery animation, so no luck.
What about using an CAOpenGLLayer with asynchronous animation oder CVDisplayLink? Would either help?
edit another thing which might be helpful information: I already used [setNeedsDisplay:YES] without my view being layer-backed, so i could easily switch between layer-backed and not-layer-backed, and i didn't have any of the problems described above, so it definitely has to do with my view being layer-backed. Everything gets a mess by just calling [setWantsLayer:YES].
So i replaced my NSTimer object with a CVDisplayLink and everything is smooth again. Although i don't actually understand why. It seems like requesting a backing layer broke the vertical sync, but it does not explain the random calls to drawRect that appeared out of nowhere.
Also i would really like to if vsync works with a layer backed NSOpenGLView and / or with a CAOpenGLLayer. I can't find any official information.
So I've got an NSSearchField hooked up directly to an NSArrayController via bindings, attached to the filterPredicate, so that without any code, the user can just type in the NSSearchField and filter the list of objects in the NSArrayController presented to him in the interface (an NSCollectionView, to be specific).
The NSSearchField is hooked up to provide live searching, so that the NSCollectionView is filtered instantly as the user types, not after waiting for a short period for the user to stop typing.
However, the problem is that this makes the interface really laggy. Typing is delayed significantly, by 0.5-1 seconds, and it seems like the NSCollectionView is trying to animate each and every rearrangement of items for each portion of the search string that the user enters.
What I'd like is for the searching to be live, but the typing in the search field to be fluid, and the results to filter as fast as possible. Is there a way to do this via bindings, or will I need to put in some custom code that triggers the filterPredicate on a separate thread?
(Note that I've got a custom sorting algorithm set up on the NSArrayController, and removing it seems to help a bit with the laggyness, but not completely.)
I would definitely go with the predicate on the separate thread. It seems you know what you have to do. Obviously blocking on the current thread is the lag issue.
Actually, it looks like you can't call setFilterPredicate: from a separate thread. It causes a crash.
It turns out that my problem was actually caused by some slow code being called over and over when setting the filter predicate, which severely slowed down the performance of the filtering. I found this by using a Time Profiler tool in Instruments on my app. That showed me which method was taking the most time, and optimizing that method fixed the lagginess issue.