macOS screen updating, part III: 2019
July 8, 2019
Five years ago, in the year of our lord 2014, I wrote about the difficulties of drawing bitmapped graphics to screen in macOS, and I revisited that issue again in the winter of 2017.
July 8, 2019
Now, I bring you what is hopefully the final installment (posted here for usefulness to the internet-at-large).
To recap: drawing bitmapped graphics to screen was relatively fast in macOS 10.6 using the obvious APIs (CoreGraphics/Quartz/etc), and when drawing to non-retina, normal displays in newer macOS versions. Drawing bitmapped graphics to (the now dominating) Retina displays, however, got slow. In the case of a Retina Macbook Pro, it was a little slow. The 5k iMacs display updates are excruciatingly slow when using the classic APIs (due to high pixel count, and expensive conversion to 30-bit colors which these displays support).
The first thing I looked at was the wantsLayer attribute of NSView:
- If you use "mynsview.wantsLayer = YES", things get better. On normal displays, slightly, on a Retina Macbook Pro's display, quite a bit better. On a 5k iMac's display, maybe slightly better. Not much.
- Using the wantsLayer attribute seems to be supported on 10.6-current.
- For views that use layers, you can no longer [NSView lockFocus] the view and draw into it out of a paint cycle (which makes sense), which prevents us from implementing GetDC()/ReleaseDC() emulation.
After seeing that enabling layers wasn't going to help the 5k iMacs (the ones that needed help the most!), I looked into Metal, which is supported on 10.11+ (provided you have sufficient GPU, which it turns out not all macs that 10.11 supports do). After a few hours of cutting and pasting example code in different combinations and making a huge mess, I did manage to get it to work. I made a very hackish build of the LICE test app, and had some people (who actually have 5k iMacs) test the performance, to see if would improve things.
It did (substantially), so it was followed by a longer process of polishing the mess of a turd into something usable, which is not interesting, though I should note:
- If you want to update the entire view every time, you can configure a CAMetalLayer properly and just shove your bits into the CAMetalLayer's texture and tell it to present and avoid having to create another texture and a render pipeline and all of that nonsense.
- If you want to do partial window updates (partial-invalidates, or a GetDC()-like draw), then you have to create a whole render pipeline, a texture, compile shaders, blah blah blah ugh.
- There's a bunch more work that needs to get done to make it all work (and adapt to changing GPUs, blah blah blah)...
As one final note, I'd just like to admonish Apple for not doing a reasonable implementation of all of this inside CoreGraphics. The fact that you can't update the 5k iMac's screen via traditional APIs at an even-halfway-decent rate is really stupid.
P.S. It does seem if you want to have your application support Dark Mode, you can't use [NSView lockFocus] anymore either, so if you wish to draw-out-of-context, you'll have to use Metal...
Recordings:
Decanted Youth - 1 - Supposed to Be -- [8:14]
Decanted Youth - 2 - (Vaguely Instrumental) Legacy -- [16:11]
Decanted Youth - 3 - (Vaguely) Round and Round -- [5:15]
Decanted Youth - 4 - The Squeeze -- [6:31]
Decanted Youth - 5 -- [3:16]
Decanted Youth - 6 - (mini cover medley) -- [10:03]
Decanted Youth - 7 -- [9:15]
Decanted Youth - 8 -- [8:26]
Decanted Youth - 9 - Trees and Mold -- [9:37]
Decanted Youth - 10 -- [4:53]
Decanted Youth - 11 -- [9:05]
Decanted Youth - 12 -- [7:02]
Posted by Daniel X on Sun 04 Aug 2019 at 10:38 from 174.56.59.x
Posted by Justin on Mon 05 Aug 2019 at 20:40 from 108.30.215.x
Posted by Chris on Fri 20 Sep 2019 at 10:13 from 204.13.44.x
Posted by Colin on Wed 27 Nov 2019 at 04:09 from 93.220.15.x
Posted by Justin on Sun 01 Dec 2019 at 10:13 from 108.30.215.x
Posted by Justin on Sun 01 Dec 2019 at 10:46 from 108.30.215.x
Add comment: