vello_hybrid: Conditionally split up rectangles into (up to) 5 smaller ones#1565
Conversation
ffae1d2 to
58364c8
Compare
taj-p
left a comment
There was a problem hiding this comment.
LGTM! Makes sense and is corroborated by my learnings made with the opaque pass investigation
| const RECT_STRIP_FLAG: u32 = 1 << 31; | ||
| /// The threshold of the rectangle size after which a rectangle should be split up | ||
| /// into multiple smaller ones. | ||
| const LARGE_RECT_SPLIT_THRESHOLD: u16 = 32; |
There was a problem hiding this comment.
Do you mind if I decrease this to 20 when I release the opaque pass PR? If we don't split up these rects, the opaque pass has no effect on fast pass rects because they're all opaque, so the size at which splitting becomes beneficial is decreased
There was a problem hiding this comment.
Sure, I don't mind, if it makes sense for you!
There was a problem hiding this comment.
Though it seems to me like having such small rectangles isn't very common (except for cached glyphs, which basically always have transparency), so not sure how big the gain would be in practice.
| .flatten() | ||
| { | ||
| let (payload, paint_packed) = | ||
| Scheduler::process_paint(&rect.paint, encoded_paints, (part.x, part.y), paint_idxs); |
There was a problem hiding this comment.
Context sharing: I believe this is an opportunity for improvement (perhaps across hybrid). For every draw command, we push a unique paint into the paint atlas. I'm wondering whether we can dedupe or whether paint heavy scenes should pass it via the vertex buffer (if so, then your draw_image API may make more sense because it doesn't carry the weight of padding/extend modes)
There was a problem hiding this comment.
Hmm I don't have concrete ideas yet, but yeah, perhaps there's something that can be done about it!
In the fragment shader for the rectangle fast path, we check whether the rectangle has any fractional part, and if so perform anti-aliasing calculations for the whole rectangle. However, the problem is that if we have a very large rectangle with some fractional edges, we will also perform those calculations for the inner parts of the rectangle, even though this is wasted work. We only really need to do this for the edge pixels.
So the idea is to split up the rectangle into up to 5 parts and make sure that the big inner part of the rectangle has no fractional edges, while the outer 4 rectangles (which have a width or height of only 1 pixels) can have fractional offsets. Doing this saves a lot of computations in the fragment shader, which is especially important on low-tier devices.
With that said, there's no free lunch: Some experimentation showed for small rectangles, the overhead that comes from having 5x as many rectangles can eclipse the savings in the fragment shader. Therefore, we only apply this optimization for rectangles that have a certain size. It's impossible to come up with a threshold that is perfect everywhere, but on my low-tier Samsung Galaxy tablet it seems to have been somewhere around 20-30 pixels. Therefore, I just went with 32 pixels here.
Here are some rough measurements I get before vs. after:
Rectangle size 50:
Rectangle size 200:
Rectangle size 500: