Add an experimental "no-copy" renderer#741
Merged
Merged
Conversation
appleguy
approved these changes
Jan 14, 2018
appleguy
left a comment
Member
There was a problem hiding this comment.
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.
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Normally
UIGraphicsGetImagemakes 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
_displaydoes basically the same thing.Another very very big win here is that,
UIGraphicsBeginImageContextcallsCGContextClearRectright 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 usesmmap(and we usecallocwhich usesmmaptoo).The API is virtually the same as UIGraphics functions –
ASGraphicsBeginImageContextWithOptionsandASGraphicsGetImageAndEndCurrentContext.You call
ASEnableNoCopyRendering()to enable this. If you call it after rendering has started, it asserts, returnsNO, and it stays turned off.Details are in
ASGraphicsContext.hWe 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.
Here's a bit of rendering a text node pre-experiment with the removed bits marked:
