Skip to content

Add setReservedOffset to crop the blurred area for mostly-static BlurViews - #270

Open
plneple wants to merge 3 commits into
Dimezis:masterfrom
plneple:master
Open

Add setReservedOffset to crop the blurred area for mostly-static BlurViews#270
plneple wants to merge 3 commits into
Dimezis:masterfrom
plneple:master

Conversation

@plneple

@plneple plneple commented Jul 21, 2026

Copy link
Copy Markdown

Hi 👋

Currently the blur RenderNode always covers the entire BlurTarget, so any change in the background causes the blur effect to be re-evaluated over the whole target for every BlurView. That's great for a BlurView that animates across the screen, but it's wasted work in what I expect is the most common case — a BlurView that is static or barely moves.

This PR adds an opt-in setReservedOffset(Integer). When set, the snapshot node is cropped to the BlurView's bounds plus the blur's required padding (conservatively estimated from the blur radius) plus the developer-provided offset, so the blur only processes that region. If the view moves outside the reserved region, the snapshot is re-recorded with a fresh crop — so the offset is the slack you give it before re-recording kicks in. When setReservedOffset is never called (or called with null), the existing full-target behavior is preserved. The change only affects the API 31+ RenderNode path.

This also addresses the existing TODO in hardwarePath about keeping the node closer to the BlurView's size instead of the target's.

Tradeoff: a BlurView that continuously animates beyond its reserved offset will sometimes re-record the snapshot, so the old default remains better for that case.

I am happy to change anything about the design if it helps land the improvement.

@plneple
plneple force-pushed the master branch 2 times, most recently from fc3698c to fbb8ffb Compare July 21, 2026 09:51
@Dimezis

Dimezis commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Hey, thanks for the PR.

Currently the blur RenderNode always covers the entire BlurTarget, so any change in the background causes the blur effect to be re-evaluated over the whole target for every BlurView

Do you have evidence that this happens without the fix, and doesn't happen with the fix?

so the blur only processes that region

Do you have evidence that the blur processes a larger region without your fix?
From what I can tell, the RenderEffect internals process only a region defined by Canvas' clip bounds:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/libs/hwui/jni/RenderEffect.cpp#39
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp#266
https://android.googlesource.com/platform/external/skia/+/refs/heads/main/src/effects/imagefilters/SkBlurImageFilter.cpp#175

So it doesn't really matter that the RenderNode has a larger size than the target View as long as clipRect is called on its Canvas (and it is).

@plneple

plneple commented Jul 26, 2026

Copy link
Copy Markdown
Author

Hey @Dimezis,

I'll get back to you on your questions, but before I dig in — could you clarify whether your skepticism is about the implementation itself or the wording of the PR description?

And what would convince you? Benchmarks, code paths, showcases, etc.? So far the only verification I have is anecdotal, from integrating this into a react-native application, but I'd be happy to set up benchmarks or an Android native sample if that would help.

@Dimezis

Dimezis commented Jul 26, 2026

Copy link
Copy Markdown
Owner

@plneple
I'm rather skeptical that there is a real problem to solve 🙂
The implementation looks good if the premise is correct.

As the TODO in code suggested, I also thought that this larger RenderNode might result in having to blur the larger area, but after digging a bit deeper, I'm not sure it's really the case.

If you can prove in some way (benchmark and/or code path) that this smaller RenderNode is more performant, I'd happily accept the PR as overall it sounds like a good idea.

@plneple

plneple commented Jul 29, 2026

Copy link
Copy Markdown
Author

@Dimezis
I am getting back with you with some analysis.

First of all, you are correct - blur filter has nothing to do with the implementation I present. Blur does only process the displayed region and samples only the pixels required to draw it.

However, there are still some gains for using smaller RenderNode.
Firstly, calling setRenderEffect causes an allocation of a separate layer. By having a smaller render node, we have a smaller GPU memory footprint.
Furthermore, later the full layer is rasterized instead of only the area we care about for the blur.

In conclusion, it seems like the main win is avoiding unnecessary rasterization, not applying the blur to a smaller area.

Also, upon inspecting the hwui / skia source code I also realized I was wrong in "conservatively" estimating the blur offset, so I have fixed that in my latest commit.

P.S. It turns out I am completely clueless how to set up a benchmark for this, but I can setup a toy example if you would like where you can see performance before / after the change on your own device.

@Dimezis

Dimezis commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Thanks, I'll try to measure the memory footprint, and will also double check if there's a way to have a BlurView-sized RenderNode while somehow maintaining the animation capabilities

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants