Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an experimental "no-copy" renderer #741

Merged
merged 9 commits into from
Jan 14, 2018
Merged

Add an experimental "no-copy" renderer #741

merged 9 commits into from
Jan 14, 2018

Conversation

Adlai-Holler
Copy link
Member

@Adlai-Holler Adlai-Holler commented Jan 13, 2018

Normally UIGraphicsGetImage makes a copy of the bitmap data. Since we are done with the context, we don't need to waste time and memory making a copy. We quickly rack up hundreds of megabytes of total allocations from our CGImages and even though they're reclaimed, it's inefficient. For uncached ASImageNode renders, this happens twice per draw (opportunity: once we generate the cached contents, return them to ASDK intact rather than drawing them.)

The end result is that ASGraphicsGetImageAndEndCurrentContext is virtually free, while UIGraphicsGetImageFromCurrentImageContext and CGBitmapContextCreateImage are two of the most time- and memory-intensive functions we call. It turns out that CALayer _display does basically the same thing.

Another very very big win here is that, UIGraphicsBeginImageContext calls CGContextClearRect right after creating the context, which is a huge waste of time with no benefit (hundreds of microseconds on an iPhone 6). The bitmap context's memory is already zero'd from the kernel since it uses mmap (and we use calloc which uses mmap too).

The API is virtually the same as UIGraphics functions – ASGraphicsBeginImageContextWithOptions and ASGraphicsGetImageAndEndCurrentContext.

You call ASEnableNoCopyRendering() to enable this. If you call it after rendering has started, it asserts, returns NO, and it stays turned off.

Details are in ASGraphicsContext.h

We achieve the vertical flip and HiDPI scaling by modifying the context's CTM. UIGraphics uses a private function CGContextSetBaseCTM. The internet and docs aren't don't say what makes the base CTM so special, but it seems to be working fine. One radar said it was used for shadowing but shadowing appears to to work. We'll keep an eye out.

Profiling results with ASDKgram on an iPhone 6. I launched, waited for it to settle, then flung down once (about 2 screenfuls' worth).

Total memory allocated during display node rendering went from 60MB to 42MB – a 30% reduction.

Runs 1 and 2 are with the feature on. Runs 3 and 4 are with it off. If you look at runs 3 and 4, you can see how, at both levels (the ASDisplayNode render and the ASImageNode contents render) we have 2 equally-sized allocations in the "begin" and "end" functions. In runs 1 and 2 there's just the allocation at the beginning.

screen shot 2018-01-12 at 8 06 38 pm

screen shot 2018-01-12 at 8 06 16 pm

screen shot 2018-01-12 at 8 06 47 pm

screen shot 2018-01-12 at 8 06 52 pm

Here's a bit of rendering a text node pre-experiment with the removed bits marked:
screen shot 2018-01-13 at 11 46 51 am

Copy link
Member

@appleguy appleguy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is historic! The only comparable improvement I recall seeing to the display system was the custom thread pool.

So the interesting thing is, I had gotten direct information from Apple that the UIGraphics API is supposed to be Copy-on-Write — specifically that the memory would only be copied if another write occurred after an image was acquired. Maybe they dropped that feature due to issues with it? No idea, but let's get rid of that ambiguity.

@Adlai-Holler Adlai-Holler merged commit 1d105c2 into master Jan 14, 2018
@Adlai-Holler Adlai-Holler deleted the AHNoBitmapCopy branch January 14, 2018 03:19
@nguyenhuy
Copy link
Member

💯 ❤️

bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
* Add "ASGraphicsContext" to skip copying our rendered images

* Zero the buffer before making a context

* Update license header

* Update dangerfile

* Make it a runtime flag

* Restore GState for good measure

* Free buffer if end without image

* Enable the experiment, and cut out the middle-man

* Fix typo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants