Skip to content
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

Add Border and CornerRadius components to bevy_ui #3991

Closed

Conversation

oceantume
Copy link
Contributor

@oceantume oceantume commented Feb 19, 2022

Objective

  • Add a simple set of components to change the visual appearance of UI nodes borders, while introducing as few changes to the actual state of bevy_ui as possible.

I know that bevy_ui will be changed to reflect requirements for the bevy editor, so I tried to make this in a way that it may still be useful if the implementation of UI layout and rendering were to change.

My personal objective with this PR was to make a simple change while getting to know more about some bevy internals.

Solution

Two new components were added. These can be added to any ui node to enable the features on them. No existing component was changed.

New components

BorderRadius

Similarly to CSS's border-radius property, this component describes a set of 4 radii, in pixels, one for each corner. This allows making a lot of different basic widget shapes without relying on textures.

Border

Similarly to CSS's border property, this component describes a width (thickness) and a color to be given to this component's border.

I was not successful at implementing border radius with a variable border width for each side of the rect. If that was supported, then it may be smarter use the Style.border field instead for the width and to replace this with a simple BorderColor component.

UI Renderer changes

Changes were made to the bevy_ui renderer to support this, including changes to ui.wgsl where the magic happens.

At the moment of starting this as a draft, all of the data used to render the borders is sent via the vertex buffer. This is not optimal at all since it triples the amount of data sent for each vertex.

If there's interest, I'd like to look into storing this information into a uniform buffer instead. This would greatly reduce the amount of data stored and uploaded to the GPU, but will add a little more complexity to the rendering code, especially since it would need to support batching.

Examples changes

UI Example

Changes were made to the "ui" example to show that you don't need an extra node to render borders anymore since you can use the Border component. Here's a close-up comparison of before and after on that example (they're the same result).

image

Button Example

Changes were made to the "button" example to showcase both Border and BorderRadius components.

image

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Feb 19, 2022
@alice-i-cecile alice-i-cecile added A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible and removed S-Needs-Triage This issue needs to be labelled labels Feb 19, 2022
crates/bevy_ui/src/render/mod.rs Outdated Show resolved Hide resolved
crates/bevy_ui/src/render/ui.wgsl Outdated Show resolved Hide resolved
crates/bevy_ui/src/render/ui.wgsl Outdated Show resolved Hide resolved
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super useful, and the code quality is pretty good! I've left a few suggestions; I'd want to see those fixed up + the more efficient rendering scheme used before approving this to be merged.

@alice-i-cecile
Copy link
Member

@oceantume can you resolve the comments that you've addressed on this PR so it's easier for reviewers to follow what still needs to be done? :)

@cometflaretech
Copy link

@oceantume I would create a BorderSide struct which contains a width and a color and then I would use pub border_side: Option<[BorderSide; 4]> just above the corner_radius. This way is much more customizable.

@oceantume oceantume closed this Feb 20, 2022
@oceantume oceantume reopened this Feb 20, 2022
@oceantume
Copy link
Contributor Author

I clicked the close button by accident😬

@cometflaretech I haven't been able to successfully implement 4 different borders in the shader code, so this wouldn't be possible at the moment. If you have some suggestions on that side I would love to have them. I tried a few things but I couldn't get the maths to actually make sense. This would be something that could be added after, when I or someone else figure it out.

@oceantume oceantume changed the title Add BorderColor and BorderRadius components to bevy_ui Add Border and CornerRadius components to bevy_ui Feb 23, 2022
@oceantume
Copy link
Contributor Author

I just pushed a commit that adds uniform buffer support. Here are a few notes on what I think could be the next steps done in follow-up PR's:

I think the extraction UI node extraction system could use a small refactor to be more similar to the sprite extraction, at least in how the loop and batching works. There's also a last_z value in the current loop that isn't used for anything after extraction.

At the moment, the ui.wgsl shader is also used for the text nodes but they don't need borders or rounded corners, so we could look into removing that overhead for text, maybe by using the shader preprocessor. However, I don't think this is a blocker because the code is pretty simple and the overhead is likely small.

Finally, border width and color for individual borders is not supported at the moment and could be very useful. I failed at getting the maths right for this (especially because it needs to work with border radius), but I'm down to look into it again soon.

@alice-i-cecile alice-i-cecile added the A-Rendering Drawing game state to the screen label Feb 23, 2022
@oceantume oceantume marked this pull request as ready for review February 24, 2022 02:02
@xiaopengli89
Copy link

If radius is 0, we can skip calculating the distance.

@oceantume
Copy link
Contributor Author

If radius is 0, we can skip calculating the distance.

Distance is also used for border color, but your point still stands if we check for both. The tradeoff is that we'll be adding branching to the fragment shader. I'll look into adding this.

nrot added a commit to nrot/bevy that referenced this pull request Mar 1, 2022
Move some work from fragment shader to vertex shader
Add branching to skip complexity when there's no rounding or border
Remove softness since we're not using it
# Conflicts:
#	examples/ui/button.rs
#	examples/ui/ui.rs
@paul-hansen
Copy link
Contributor

paul-hansen commented Jun 22, 2022

Hey @oceantume I'm just a random user that wants to see this merged. It looks like this is pretty out of date with main. Would you be able to update this so it has a better chance of being merged?

Looks to me to be the only thing holding this up, lmk if there is something else. I'd be willing to take a stab at updating it if you aren't able to.

@oceantume
Copy link
Contributor Author

Hey @oceantume I'm just a random user that want's to see this merged. It looks like this is pretty out of date with main. Would you be able to update this so it has a better chance of being merged?

Looks to me to be the only thing holding this up, lmk if there is something else. I'd be willing to take a stab at updating it if you aren't able to.

I have a feeling this wasn't merged because it made a lot of changes to the pipeline and shaders.

I don't have time to update it at this moment (I'm moving in 5 days), but I'd be willing to look into it. However, I wouldn't be surprised if Cart didn't want to merge it before seeing if there's a better way to implement the pipeline changes for this.

@paul-hansen
Copy link
Contributor

Sounds good, thanks for the update. I'll probably leave it alone then, pipeline decisions are going to be bit over my head still. If someone can confirm we don't need changes to the approach lmk and I might be able to get what you already had working.

@alice-i-cecile alice-i-cecile added the S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! label Jul 27, 2022
@mockersf
Copy link
Member

By adding a new Border component outside of the existing Style component, this border width won't be used for the layout and will overlap neighbour nodes

@alice-i-cecile
Copy link
Member

My preferred fix would be to strip the border field from Style, and then populate taffy's version internally during the interface. This information is distinct, and belongs in its own component (so then we can do things like reuse the component for sprites).

@mockersf
Copy link
Member

mockersf commented Jul 27, 2022

the border from style is a lot more rich than a f32 from this PR, and that would mean handling all those cases for rounded corners

@Weibye
Copy link
Contributor

Weibye commented Aug 1, 2022

By adding a new Border component outside of the existing Style component, this border width won't be used for the layout and will overlap neighbour nodes

I think we should hold off on this until we have figured out #5511, then we should look into whether it makes sense to populate the new border component with field required for border rendering (or if they should be entirely separate)

My hope here is that the user can define border in one place and that it gets picked up by both layout and rendering

@nicoburns
Copy link
Contributor

Just wanted to note that work on #5511 is now on hold and as such this PR is not blocked. Regarding the API, I think the existing border property in Style should be renamed to border_width and either:

  • border_color and border_radius properties added directly to Style
    or
  • border_width should be moved to the new Border component alongside the border_color and border_radius properties.

I do think ideally this would support setting different widths for each side. It should also support corner clips that extend beyond the border and into the main content region if that is not already supported.

Finally, it should also be noted that there is an implemention for borders without radii in #7795 which could potentially be combined with this work.

@oceantume Do you have time to update this and drive it towards being merged?

@togetherwithasteria
Copy link
Contributor

togetherwithasteria commented Mar 29, 2023

Just wanted to note that work on #5511 is now on hold and as such this PR is not blocked. Regarding the API, I think the existing border property in Style should be renamed to border_width and either:

* `border_color` and `border_radius` properties added directly to `Style`
  or

* `border_width` should be moved to the new `Border` component alongside the `border_color` and `border_radius` properties.

I do think ideally this would support setting different widths for each side. It should also support corner clips that extend beyond the border and into the main content region if that is not already supported.

Finally, it should also be noted that there is an implemention for borders without radii in #7795 which could potentially be combined with this work.

@oceantume Do you have time to update this and drive it towards being merged?

FYI. I have ported the PR code to 0.11.0, over at my Bevy fork here
https://github.com/project-flara/bevy/tree/ui-renderer-0.11.0

@togetherwithasteria
Copy link
Contributor

togetherwithasteria commented Mar 29, 2023

I've managed to get things compiled and running, though I can't see the border and border radius..

This is the first time I'm handling shaders. T_T

@oceantume
Copy link
Contributor Author

Hi, I would check with Bevy members before putting too much work into this. If I remember correctly this was blocked for a long time and semi-declined because of the amount of renderer code required to make it work.

There's also a good question of whether this is really how it should be done, especially the "fake anti aliasing" done in the shader.

@togetherwithasteria
Copy link
Contributor

There's also a good question of whether this is really how it should be done, especially the "fake anti aliasing" done in the shader.

I personally don't mind any specific ways to do it, as long as it works and it works well for now.

It's just weird that Bevy doesn't have a border radius support, it's certainly not great for people who would like to make pretty UIs.

mockersf pushed a commit that referenced this pull request Jun 14, 2023
# Objective

Implement borders for UI nodes.

Relevant discussion: #7785
Related: #5924, #3991

<img width="283" alt="borders"
src="https://user-images.githubusercontent.com/27962798/220968899-7661d5ec-6f5b-4b0f-af29-bf9af02259b5.PNG">

## Solution

Add an extraction function to draw the borders.

---

Can only do one colour rectangular borders due to the limitations of the
Bevy UI renderer.

Maybe it can be combined with #3991 eventually to add curved border
support.

## Changelog
* Added a component `BorderColor`.
* Added the `extract_uinode_borders` system to the UI Render App.
* Added the UI example `borders`

---------

Co-authored-by: Nico Burns <nico@nicoburns.com>
@alice-i-cecile
Copy link
Member

Border radius was added in #12500.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible S-Adopt-Me The original PR author has no intent to complete this work. Pick me up!
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

10 participants