Re: lockFocusIfCanDraw behavior in layer-backed mode
Re: lockFocusIfCanDraw behavior in layer-backed mode
- Subject: Re: lockFocusIfCanDraw behavior in layer-backed mode
- From: Matt Jacobson <email@hidden>
- Date: Mon, 17 Dec 2018 11:39:48 -0800
Hi,
> On Dec 15, 2018, at 7:07 AM, Tor Arne Vestbø <email@hidden> wrote:
>
> Hey hey,
>
>> On 15 Dec 2018, at 01:38, Matt Jacobson <email@hidden> wrote:
>>
>> You were probably calling -lockFocusIfCanDraw
>> <https://developer.apple.com/documentation/appkit/nsview/1483285-lockfocusifcandraw>,
>> which does not say that.
>
> Piggy-backing on this thread, what does lockFocusIfCanDraw actually do in
> layer-backed mode?
>
> Without layer-backing, lockFocusIfCanDraw could be used to draw into the
> NSWindow surface outside of the normal display-cycle, in a push-fashion.
>
> With layer-backing, it seems to set up some sort of “Null” context, and calls
> setNeedsDisplay? Is the context it sets up valid and you should draw to it
> like normal, and then unlockFocus And then AppKit will flush that context on
> the next display cycle? Is there then a way to detect in the next
> display-cycle (drawRect:/displayLayer:) that its a result of
> lockFocusIfCanDraw, so you don’t actually need to redraw everything once
> again?
No, sorry: it’s not possible to detect that a redraw was triggered by
-lockFocusIfCanDraw.
Even if it were, it’s still unsafe to ignore the rect passed to -drawRect:.
That’s because the system might require you to redraw a larger area than what
your code thinks needs to be redrawn.
> I guess this also means it’s not as synchronous as the non-layered mode where
> you could use [NSWindow flushWindow] to push the result to the screen, eg:
>
> for (int i = 0; i < animationFrames; ++i) {
> lockFocusIfCanDraw
> draw to NSGraphicsContext currentContext]
> unlockFoucs
> [NSWindow flushWindow]
> }
This style of drawing won’t work in new apps. You’ll notice that
-lockFocusIfCanDraw, -unlockFocus, and -flushWindow are all deprecated in the
Mojave SDK.
> (I know this is a bad ida, but) is there a way to achieve the same
> synchronous flush with layer-backing?
No.
The imperative view drawing model you’re describing has long been discouraged.
Among others problems, imperative drawing disrupts the drawing of overlapping
views (both siblings and superview–subview pairs), is incompatible with
layer-backed views, and prevents system drawing optimizations.
Furthermore, apps need to be able to redraw at the system’s request, too. That
means that any imperative drawing code would need to be reflected in -drawRect:
anyway, for when the system requests a redraw.
For those reasons (and others), imperative view drawing doesn’t work in apps
linked on the Mojave SDK.
✻ ✻ ✻
If it’s really difficult to avoid imperative-style view drawing, you could
consider options like:
imperatively drawing to an offscreen buffer, and then using NSBitmapImageRep or
CGImage to draw from your offscreen buffer in -drawRect:
using -[NSImage initWithSize:flipped:drawingHandler] to capture drawing code
into a block; by drawing that image in -drawRect:, you’ll defer execution of
the drawing code until then
Both of these options are tricky, though. With the offscreen buffer, you’ll
need to manage the scale factor and colorspace yourself; with the
drawing-handler image, you might be surprised that your code executes at a
different time than before.
And both of those approaches require changes to -drawRect: anyway.
> The session doesn’t go into what do if you want to draw synchronously outside
> of the display cycle though. Is it enough to setNeedsDisplay and then call
> [NSView displayIfNeeded] or [CALayer displayIfNeeded]?
All you need to do is call -setNeedsDisplayInRect:. (You could instead call
-setNeedsDisplay: if you need to redraw the entire view.) The system will
ensure that your view is redrawn the next time the window is committed to the
screen (which typically happens when the runloop runs again).
The system ensures that all views in the app that redraw in a given update will
hit the screen at the same time.
> I guess the latter since I don’t want AppKit to walk the view hierarchy and
> display other views (unless it’s smart enough to treat a layer-backed view as
> not needing that behavior).
It is dangerous for a view to assume that it is the only view that needs to be
updated in the entire application. There may be views (or windows) in your
application that the code you’re writing doesn’t specifically know about. If
there aren’t any such views now, there could be in the future. Such views (or
windows) might be provided by frameworks you use—or by the system itself.
I hope this helps!
Matt
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden