Repository navigation
Let Skia cache the mask of a blurred Android inset box shadow - #59083
YevheniiKotyrlo wants to merge 2 commits into
Conversation
Device measurement — before / after#59081 alone — every frame of an animated background renders each blurred inset shadow's mask offscreen and blurs it: 530 render passes per frame for 150 views on a Galaxy S23. This PR — 37: Skia draws all but seven of the masks from its cache each frame.
RNTester release builds for SceneThe Device evidence — before / after#59081 alone — a blurred inset shadow drawn as a double round rect. This PR — the same shadow drawn from its cached even-odd path: the pixels that differ all lie inside the blur, by at most 4 of 255 per channel.
Galaxy S23 (Android 14, API 34), 1080×2340 at density 480, the scene above with |
InsetBoxShadowDrawable and OutsetBoxShadowDrawable resolved the border radii and built new RectF, Path and radii arrays inside draw(), so every redraw of a view's background, every frame of an animation that touches it, allocated them again and handed the canvas a new Path. Both drawables now keep that geometry in fields and rebuild it only when its inputs change: on a bounds or layout direction change, and when borderRadius or borderInsets is assigned. An alpha or color filter change only redraws.
A blurred inset shadow is drawn with drawDoubleRoundRect, and Skia turns a double round rect with a mask filter into a new volatile path on every draw, which it never caches: the blurred mask is rendered again, offscreen, every time the view is drawn. Draw a blurred inset shadow from a cached even-odd path instead, so Skia can key its blurred mask on the path, the blur and the matrix. An unblurred shadow keeps drawDoubleRoundRect, which Skia draws analytically.
5d65b93 to
e32e6b2
Compare


Summary:
InsetBoxShadowDrawabledraws its shadow withdrawDoubleRoundRect. With a blur, Skia's GPU backend turns a double round rect into a new volatile even-odd path on every draw, and it caches a mask-filtered mask only for a shape it can key, so a blurred inset shadow renders its mask offscreen, and blurs it, every time the view draws.A blurred inset shadow now draws an even-odd path the drawable builds with the rest of its geometry, so Skia keys the blurred mask on the path, the blur and the matrix, and draws it from its cache on the next frame. An unblurred inset shadow keeps
drawDoubleRoundRect, which Skia draws analytically.This builds on the cached geometry from #59081 and includes its commit.
Changelog:
[ANDROID] [FIXED] - Blurred inset box shadows no longer render their mask offscreen on every draw
Test Plan:
BoxShadowDrawableTestgains five cases: a blurred rounded and a blurred rectangular inset shadow hand the canvas one even-oddPath, the same instance on every draw (on #59081 they draw no path at all); an unblurred inset shadow still draws a double round rect and no path; and a blurred inset shadow follows bounds growing from empty and a radius changed in place and reassigned, compared with a freshly built one../gradlew :packages:react-native:ReactAndroid:testDebugUnitTest -PenableWarningsAsErrors=true: 781 tests, 0 failuresnode ./scripts/format-kotlin.js --check: cleanOn device (evidence), RNTester release builds of #59081 and of this branch, 150 views with an animated background and
inset 0 2px 6px: Skia's render passes per frame fall from 530 to 37 on a Galaxy S23 (Android 14, API 34, Adreno 740), and the median frame from 21.0 to 13.6 ms there and from 395.6 to 19.5 ms on a Mi MIX 3 (Android 10, API 29, Adreno 630). The no-shadow control stays level, and the static frame differs only inside the blur, by at most 4 of 255 per channel (the before/after pair is in the same comment).I didn't run the full Android
build, the template and HelloWorld app builds, or the Android e2e jobs locally.