-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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 rounded edges to Rectangle #3121
Conversation
Thanks for opening the PR. Would you mind changing the title name to be more specific about what the PR is doing (instead of which issue it fixes)? :) |
Amazing thanks! Can you please put example code into the |
When we say "tests included" we mean unit tests, not example code. |
Ok, I found what you mean. Expanded cmd/fyne_demo/tutorials/canvas.go. |
remove example/flexrect.go create test in rectangle_test.go Changes to be committed: modified: canvas/rectangle_test.go modified: cmd/fyne_demo/tutorials/canvas.go deleted: example/flexrect.go modified: internal/painter/gl/draw.go
Which lint tool should be used if any? |
The ones used in the static analysis action should always be used. |
This is pretty cool. Looking at the code, I realized that maybe it will lead to a faster frame rate when using the web backend. Sadly, something is broken there with this patch. I think it is a bug in the gl-js layer and not in this patch. I am going to look more into it, but please do not land this patch until confirmed that it work for the web backend properly. |
I figured out that there is no GL ES variant of the shader which lead all other backend to fail. I kind of half fixed it locally, but then I see errors in the web console:
|
I havn't looked at GL ES so far. Shaders for GL ES are not defined and built at the moment nor implemented in eg. gl_es.go->Init(). My next steps are to split the method flexRectCoords() in draw.go to fullfill the lint checks. I hope the GL 110 and GL ES are compatible. I think in that case only the calling stack for GL ES must be changed. |
Yes, the GL and GL ES shader between 110 and 100 are very close. |
It is a very simple shader, there won't be a problem with OpenGL100 and GL ES 110. The shape is just a simple rectangle, no complexity there. Indeed the way it is currently written, it is the same radius for all corner. The benefit of that shader is that you also get linear gradient for the stroke and fill. I kind of think your technique is closer to my other proposal to have a bunch of very simple independent triangular shape and build complex shape from them. I do think both work, but having a dedicated shader just for rounded rectangle would pay off considering the amount of time rounded rectangle shape are used in modern UI. |
There is a first version of GPU rendered stroke rectangle. This prototype doesn't have round edges. The normalization of vertex shader is calculated on the GPU too. |
Just tested, it does work (as you say no round edges). This is on the github.com/renlite/fyne@develop branch for whoever want to test next. |
@Bluebugs Thank you for the test. |
Nice, it does work for me with the rounded rectangle. Maybe something to pay attention to when doing the PR, but the commit log looks weird. |
Looks like this for me, regardless of what card I'm using (notice the buttons) |
@ErrorNoInternet Thanks for the response! I assume flickering is gone. |
Yup, flickering is gone. |
I thought it has something to do with scale and went through the code. I didn't find the reason in the fragment shader. |
It seems the real size of the screen/window in the fragment shader is not the submitted frame size but a scaled form. Maybe I have to consider the scale in the fragment shader. |
I haven't looked at your code, but you might need to get the exact pixel size and not the windows pseudo pixels size which is scaled. I have found that a bit confusing as it is hard to see when it is scaled and when it is not. In your case, as things are sent to GL, you want to have the real pixels size. |
I can confirm it is a scale issue, you can try with different FYNE_SCALE value different from 1 and you will see the issue. |
Scaled values (our main coordinate system) are Potential confusion arises later because OpenGL is working with floats from 0.0 and 1.0. However that is specific to the GL painter and not something that should impact other code. |
Vertex coordinates are working different from fragment coordinates. Fragment coords can be normalized to 0.0 -> 1.0 if the real size of the screen is known, but this is not necessary. From this view some var names in the fragment shader are not correct, and should be changed later. |
The code has been updated: pixScale is submitted to OpenGL and used in fragment shader. It has no impact on my hardware with scale 1. |
This does work for me and look pretty good. It is definitively reducing massively the amount of texture uploaded to the GPU. I think that with the same shader, you should be able to provide the implementation for circle to be fully running on the GPU too without texture upload. Just a quick review of the shader. Division is slow and costly and 2.0/frame_size.x along with 2.0/frame_size.y are not changing for the entire run of the shader. I guess you can just precalculate them and just do vec4(vert.xscale.x - 1.0, 1.0 - vert.yscale.y, 0, 1) where scale.x == 2.0/frame_size.x and scale.y == 2.0/frame_size.y. A shader get its entire code, including all the if branch, executed for every run of the shader. The For people that are interested in reviewing the PR and might now know so much about the details of shaders, the way to think a bit about shader, they are executing things for multiple pixels, geometry, ... in parallel. That parallel execution is done using mainly a Single Instruction Multiple Data design. To make it work with Overall this feel very close to a PR and very early in our release cycle, would be amazing to get! |
Does it work for @ErrorNoInternet and @andydotxyz too? |
It seems to work for me on Intel UHD Graphics 620 but the grey rectangles go red when I resize the window (in the example application). |
Thanks, the grey to red change is ok, it is the only possible type of debugging on the GPU. This will be deleted later. |
Thank you for your feedback. That seems to be also ok. The last rect without round edges is not correct but this is because I did not scale the size in the shader for this rect type. There are open questions to discuss (as mentioned by @Bluebugs) whether to intergrate the simple rect or to write a separate shader. The simple rect (with stroke) is massively used so a simpler shader would save power.
I would say this behavior is ok too but I can't anwer this question. |
This is absolutely true.
Yes, when moving window between screen with different scale, the window min size will be recalculated and most of the time it will be different due to a difference in pixels density that lead to a bigger window. |
@Bluebugs @andydotxyz
Yes, this is a good input to reduce calculations in the shaders for variables that are constant through all threads. I know that the fragment shader creates for all pixels of the rendering area a thread to paint the color, but is this true for the vertex shaders too ? I don't know if the vertex shader uses only the number (threads) of used points - in our case 6 = two triangles - or the whole number of pixels. If vertex shaders creates threads for all pixels too then it would be better to make the normalization in Go and not in OpenGL.
Yes, this was my plan to do it with a new PR. Maybe with an own fragment shader. I think there is a faster version for circle only. @Bluebugs @andydotxyz @Jacalz @ErrorNoInternet
@andydotxyz could you please test the example /cmd/rect_test/main.go (branche develop) whether flickering is gone on your hardware? |
Absolutely, will be easier to do a proper review from a clean start.
For the vertex shader, it should be per vertex. So having a bit of optimization there is still a good thing, but no need to do head scratching one :-)
I agree, there is a faster one for circle and it could enable adding more option later to it (like partial arc and stuff). Still, this is already a pretty good step forward, so I would be perfectly ok with just reusing the rounded rectangle shader for now.
I think having multiple stroke width for each side of the rectangle to be maybe a bit unnecessary at this stage. All the modern design I have seen are using the same stroke around the rectangle. It might be a marginal use case that would be better left to a generic vector drawing primitive.
I don't think you need a new primitive, you just need to check in the painter if the radius is != 0 and switch the shader accordingly. I prefer to have less primitive exposed, but with more feature and put all the optimization logic in the painter as much as we can. |
When run on macOS I see this:
Yes please, a nice clean start with the new code. It may be worth noting that if
I don't think the API should be designed around implementation details. We can internally load two different shaders based on whether |
@renlite Works perfectly fine on both my monitors! |
Thank you for the tests. I will include the OpenGL ES path and the radius in percentage and then make a PR. |
Description:
#1090
Implementation of a (Round)Rectangle as primitive Open GLObject.
Limitation: Shapes with stroke and only one segment don't work properly. As a work around for shapes with opaque colors you could use composition. As this is not the main use for this canvas object I don't plan to implement it.
Checklist:
Where applicable: