-
Notifications
You must be signed in to change notification settings - Fork 317
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
Compute shrink-to-fit width for flex blocks #559
Conversation
c68bfdc
to
f51b5a1
Compare
Thanks a lot for the pull request, enhancements to this part of the layout engine is very much welcome. Can you expand a bit on how this relates to the specific issues in #552? Before and after comparisons would be helpful. If you are still working on this, let me know when you feel it is ready for review. |
The implementation passes my own tests. In particular, I was able to correctly render a pretty complex UI in my MMO game. I made a few corrections to the PR recently after I discovered a couple more corner cases. The major one was an assertion in the flex layout engine where it incorrectly estimated size of nested elements when both element and container had an auto width. Examples that cover those corner cases are available below. From the correctness standpoint, you can review and even merge this PR right away. I'm pretty confident that it won't break existing applications and can only make it better for auto-sized flex elements. However, from performance standpoint, it's terribly slow. Existing applications may be hit by the combinatorial explosion that you highlighted in #552 (comment). The engine tries to compute the same layouts of nested elements with the same parameters over and over again. I think if we do some memoization of computed layouts to return the same result if called with the same parameters, it would immediately yield enormous performance benefits reducing algorithmic complexity from O(2^N) to O(N). I would appreciate it if you could review the code as is (maybe move it to another branch) and then perhaps help with caching/memoization, because it's going to be a pretty deep change to the layout engine. If you have other ideas, I'd love to hear them. Case 1
Case 2
Case 3
Case 4
And a failed assertion in the console:
Case 5
Case 6
|
Actually, you know what? it turned out that caching shrink-to-fit width is pretty simple. Check it out - d9a4627. Layout performance immediately went back to normal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach looks simple and effective, nice! :) The spec goes on about handling different flex factors and such, but a simpler approach could be more suitable for us. Regardless, this is non-breaking behavior, as we didn't support shrink-to-fit here previously. And then we could improve later if we find a case for that.
I believe we might have some issues with handling percentage-based sizing here (and generally in shrink-to-fit contexts?), which is something we might want to improve later. But this probably requires more thorough layout engine-wide changes implementing stronger typing for indefinite sizing and sizing constraints.
I ran this through all of our own and generated layout tests to confirm we didn't break anything. Most tests are equal to before, only two CSS flex tests had been changed. The changes being improvements - now both of them matches their reference! Attaching screenshots of the changes at the bottom here .
Your examples here look great too, appreciate all the details. I wonder if you could add some, or all of these to the VisualTests? That way we ensure they don't break in the future.
I only have some minor comments for this first commit, otherwise I'm mostly ready to merge this.
When it comes to the caching commit, this is something I have very much been interested in looking at for a while. However, that is also a very big change (code is small but implications are large), and requires a lot more consideration. We should move that to a new PR and discuss things thoroughly.
Yellow frame is before, document to the right is reference.
@@ -315,10 +336,11 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector | |||
RMLUI_ASSERT(initial_box_size.y < 0.f); | |||
|
|||
Box format_box = item.box; | |||
if (initial_box_size.x < 0.f) | |||
if (initial_box_size.x < 0.f && flex_available_content_size.x >= 0.f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does flex_available_content_size.x < 0.f
mean indefinite size? Does it carry the meaning of a max-content sizing context, or can it be negative any other way? I think we should give this condition a named bool if we can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly I don't fully understand this code and can't exhaustively list all cases when it can happen. It definitely happened in shrink-to-fit context, and there it meant infinite max-content box. What I can say for sure that if a negative width makes its way to SetContent, then it will definitely fail an assertion later on. See my original comment. I was able to trip that assertion even before my changes, so I think this change is an improvement. Whether it's universally correct, I don't know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I believe generally this is considered indefinite, which in the context of available space would mean infinite. The spec goes into a lot more detail for these calculations, but his should definitely be an improvement.
I think it is fine as it is written here.
I'm happy with these changes now, thanks! The only thing I am hoping for is to add some or all of these cases to the visual tests. |
Just out of curiosity, does this change allow |
Hey @Paril, I did not test if my change is sufficient to support @mikke89 Could you merge it as is and help with visual tests as a follow-up? My environment is non-standard - I use bazel to build RmlUi instead of cmake, and don't have much time figuring out how to install all the necessary dependencies on Windows. |
Surprisingly, I did do this, and it appeared to work - but I don't know enough about the codebase to know if that's the only thing that prevents it from working properly, heh. See #577 (comment) I might be able to help with the visual tests, since I pretty much exclusively use flexbox in any web development that I do. |
I don't think it's that surprising. IIUC |
Keep in mind it was two places - I had to fix this line as well: RmlUi/Source/Core/Layout/LayoutDetails.cpp Line 453 in d0de094
|
Done |
Cool, let me re-merge and see how it looks. I'll edit this comment when I test it. EDIT: yep, this fixes #577 with inline-flex; it is now shrinking to content properly. |
Great, thanks a lot! This is a very nice addition. It definitely makes sense to enable this behavior for inline-flex too. I'll go ahead and add those visual tests. |
Visual tests added here: ab20b36 Also, added a benchmark: 37d0001 This PR didn't affect the numbers for our existing benchmarks. After adding some new shrink-to-fit cases, naturally we see a slow-down in these cases, compared to the before the changes in this PR, where we just didn't really format it. Before:
After:
As discussed earlier, the nested case is definitely something to be aware of. |
Refs #552