Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upUse a better approximation for the border corner shader. #1791
Conversation
|
This is an approximation, itself on top of another approximation. I tried the "real fix" and the difference is hardly noticeable with code that is a lot messier so I am inclined to go with this solution that is imperfect but already much better than what we currently have. r? @glennw |
|
Thanks for looking into this! A couple of comments (well, questions actually, since I haven't looked at this stuff for a while):
|
I think it is. Not quite from looking at the code but it's visible from the screen capture from issue #1750 where I drew some circles on top of Markus's original picture, the distance looks very much like it is sampled from the corner of a pixel rather than the sample, and my interpretation is that it is because we are interpolating the positions from a varying rect that is snapped in the vertex shader (need to check).
Hm. I am not 100% certain but I think that we don't need a scaling factor since we are operating in device pixels there, or if we do I think that other code around it also need to be adjusted. I'll double check.
Yeah, I meant to submit this for feedback rather than final review (I wasn't 100% confident about the popularity of the approximation), I'll come back with some screenshots and update reftests tomorrow. |
|
All the SDF operations for clipping etc always operate in local (CSS) space. So those distance calculations you modified are not in device space. |
Right, I managed to wrap my head around this and indeed, this is all in css pixels with This version of the patch fixes that by using the the average of the width and height of I kept In the image below, before/after this PR, what firefox renders for one of the corners as a reference and the same corner with a pixel ratio of 8 to show that it doesn't break this time. |
|
There are still a few reftest failures, apparently from the change in how a side's color transitions into the other. I'm looking into that. |
|
The groove and ridge border reftest failures were really weird: the tests were already not passing on my computer and started not passing on try with this PR I have no idea why. The modulated colors (vColor00/01/11/10) of the border had their components between -0.3 and 1.3 instead of 0.0, 1.0, which meant that while the final color would be clamped to the proper range, intermediate computations like the transition between these colors were saturating which produced wrong results. The last commit fixes that, even though it should not have been related to the aliasing of the border radius. |
| vColor01 = vec4(color0.rgb * modulate.y, color0.a); | ||
| vColor10 = vec4(color1.rgb * modulate.z, color1.a); | ||
| vColor11 = vec4(color1.rgb * modulate.w, color1.a); | ||
| vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(1.0)), color0.a); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nical
Oct 5, 2017
Author
Collaborator
It's not necessarily incorrect for modulate to be out of the 0..1 range if we want the modulation to brighten the color. I am not sure whether we need that (I assume we do, otherwise maybe the bug is in the values we pass to the shader)
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nical
Oct 5, 2017
Author
Collaborator
Under the assumption that we can pass modulate components greater than 1, this is the place that makes most sense to me since this is where the base colors are computed. Any later computation can "get it wrong" by assuming they are in the range that goes according to their semantic (colors). Where would you have done the clamping?
This comment has been minimized.
This comment has been minimized.
kvark
Oct 6, 2017
Member
It largely depends on whether the semantics of border colors is defined for vertices as opposed to pixels. I assumed that W3C would care about pixel colors, while the fact that GPU have vertices and interpolate between them is an implementation detail. In this case, the clamping should be done for the pixels, not here.
This comment has been minimized.
This comment has been minimized.
nical
Oct 6, 2017
Author
Collaborator
These are flat varying variables so there's no interpolation at all. Even if there was, it would be incorrect to interpolate between the the non-clamped values (you'd end up with colors constant over a certain range as they are capped at the brightest value until the result of the interpolation gets to the correct range and then it would start interpolating. This is actually the equivalent of one of the bugs this PR is fixing but at the vertex interpolation level instead of the mix between two sides of the border.
|
I'll update the reference images to match the adjusted transitions between the sides. |
|
Wow, those reference images look so much better! |
|
@glennw the PR is ready for a real review this time. |
|
a few more annoying questions for you |
|
|
||
| // A half device pixel in css pixels (using the average of width and height in case | ||
| // there is any kind of transform applied). | ||
| float half_px = 0.25 * (fw.x + fw.y); |
This comment has been minimized.
This comment has been minimized.
kvark
Oct 6, 2017
Member
it sounds to me that 0.5 * length(fw) would be slightly more appropriate, which is almost similar to aa_step
This comment has been minimized.
This comment has been minimized.
nical
Oct 6, 2017
Author
Collaborator
if you consider fw to be "kinda like a device pixel ratio in x and y", length(fw) gives something that is the diagonal of the rectangle described as the pixel ratio whereas here we want something that is equal to a side of that square (the aa math here makes the approximation that fw.x and fw.y are somewhat similar). There is an average of the two as an effort to not do something bad with some transforms.
Since this is all on top of approximations I first felt like it wouldn't matter too much and tried to just use 0.5*length(fw) (even if it overshoots a little), and the results weren't as good so I ended up with this instead.
|
|
||
| // SDF subtract main radii | ||
| float d_main = max(d0, 0.5 * afwidth - d1); | ||
| float d_main = max(d0, aa_step - d1); |
This comment has been minimized.
This comment has been minimized.
kvark
Oct 6, 2017
Member
seems a bit weird that we add a half pixel to distances only to then subtract the aa_step from them. Isn't it a bit redundant? Shouldn't we instead just work with pure distances and have a single step constant that takes into account the difference between the current aa_step and half_px?
This comment has been minimized.
This comment has been minimized.
nical
Oct 6, 2017
Author
Collaborator
I agree that we can probably rethink this code and make it a bit simpler.
Don't worry, this stuff is subtle, we'd better make sure it is well understood. |
|
There's a clear visual improvement in all the updated reftests. @bors-servo r+ |
|
|
|
Thanks @nical for looking into this! |
Use a better approximation for the border corner shader. Fixes #1750. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/1791) <!-- Reviewable:end -->
|
|
Use a nicer approximation for anti-aliasing. Since changing `k*length(fwdith(pos))` to use `0.7` instead of `0.5` greatly improved the visual quality of the corner borders in #1791, I am following up with the same change in the other places where we use distance to edges for the anti-aliasing. In the process of doing that I had to fix the cs_clip_rectangle shader which had two problems: - the aa step was added to the distance which makes no sense (the distance is in CSS pixels while the aa step can be seen as a factor of the CSS to device pixel ratio) and was the reason reftests/transforms/rotated-clip.yaml looks bad. - `smoothstep(0.0, afwidth, 1.0 - current_distance);` doesn't make much sense (to me) either, we want to invert the result of the smoothstep rather than what we pass to it. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/1822) <!-- Reviewable:end -->

nical commentedOct 2, 2017
•
edited by larsbergstrom
Fixes #1750.
This change is