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

round off subpixel offsets from scroll origins #2738

Merged
merged 1 commit into from May 24, 2018

Conversation

@lsalzman
Copy link
Contributor

lsalzman commented May 8, 2018

This is to address downstream Gecko bug https://bugzilla.mozilla.org/show_bug.cgi?id=1458921

The problem is that when both the glyphs and the transform contain a subpixel offset, they can combine together to produce visual artifacts. If you imagine two glyphs, one with offset 0.3, and one with offset, 0.2, if positioned at scroll offset 0, they both round to device pixel 0. If the scroll offset becomes 0.2, suddenly the 0.3 rounds up to device pixel 1, while the 0.2 glyph still rounds to 0. So this can contribute to the appearance of a wavefront of messed up hinting as you slowly scroll along.

We're not necessarily doing things differently in the text shader itself, as both Cairo and Skia round the position after the transform is applied. However, in Gecko, we ensure that never let Cairo or Skia see a subpixel offset from scrolling so this issue never arises, despite them handling rounding of glyph positioning at a similar time during rasterization.

Thus, if we just ensure the scroll offset is rounded up front like in Gecko, the problem disappears.


This change is Reviewable

@kvark
Copy link
Member

kvark commented May 8, 2018

Looks reasonable! Could you start a try push with this change?

@mstange
Copy link
Contributor

mstange commented May 8, 2018

Is the rounded value in screen space or in local space? The rounding needs to happen in screen space.

@gw3583
Copy link
Collaborator

gw3583 commented May 10, 2018

This does seem like the right place to do it, I think. I'm fairly certain at some point in the past WR used to do this already, maybe it got lost somewhere along the line. You should probably scale it by the device pixel ratio (should be available in a parent function somewhere), which will make the rounding to a single device pixel.

@lsalzman lsalzman force-pushed the lsalzman:no-scroll-subpixel branch from b0c3da8 to 8eac515 May 16, 2018
@lsalzman
Copy link
Contributor Author

lsalzman commented May 16, 2018

Okay, this is v2. As the new commit message implies, there was a lot of messy uncertainty about glyph subpixel positioning going on, because we were treating them in local space. That really doesn't work well under transforms. Instead, I made GlyphKeys record subpixel positions in device-space. In the process I moved the nastiness of subpixel directions out of the API (as Skia does - you can only toggle it on or off, but the direction is automatic). Likewise, I made the axis handling of subpixel directions more like how Skia does it - unless the transform is axis-aligned, you get subpixel precision on all axes.

Finally, the cherry on top, is that I am now handling snapping of the scroll node transform in the text shader itself. Any other approach that tried to modify the transform up front affected the other shaders in a way that made Gecko try very very unhappy with regressions. With this approach, all we have is some non-threatening fuzzable differences on a couple tests, but otherwise it still fixes the original bug in question here.

@lsalzman lsalzman force-pushed the lsalzman:no-scroll-subpixel branch from 8eac515 to f4f3ea1 May 16, 2018
@bors-servo
Copy link
Contributor

bors-servo commented May 17, 2018

The latest upstream changes (presumably #2763) made this pull request unmergeable. Please resolve the merge conflicts.

@lsalzman lsalzman force-pushed the lsalzman:no-scroll-subpixel branch from f4f3ea1 to 9fb83a5 May 17, 2018
@gw3583
Copy link
Collaborator

gw3583 commented May 21, 2018

Reviewed 28 of 28 files at r1.
Review status: all files reviewed at latest revision, all discussions resolved.


webrender/src/prim_store.rs, line 817 at r1 (raw file):

            for (i, src) in src_glyphs.enumerate() {
                let layout_offset = src.point + self.offset;
                let world_offset = font.transform.transform(&layout_offset);

It seems like if there is a property animation occurring, this would never get the new transform, since this branch would be skipped by the glyph_keys.is_empty() check above. I guess we need to detect if this has changed since we last calculated the glyph keys to invalidate them.


webrender/src/glyph_rasterizer/mod.rs, line 142 at r1 (raw file):

    pub fn get_subpx_dir(&self) -> SubpixelDirection {
        if self.skew_y == 0.0 {

Should these be epsilon checks due to float precision?


Comments from Reviewable

@gw3583
Copy link
Collaborator

gw3583 commented May 21, 2018

This generally looks great to me. There's a couple of issues noted above, and then we'll need to get this rebased + do a gecko try run. Then this should be ready to get merged.

@lsalzman lsalzman force-pushed the lsalzman:no-scroll-subpixel branch from 9fb83a5 to 9e8f464 May 23, 2018
@lsalzman
Copy link
Contributor Author

lsalzman commented May 23, 2018

Review status: 24 of 28 files reviewed at latest revision, 2 unresolved discussions.


webrender/src/prim_store.rs, line 817 at r1 (raw file):

Previously, gw3583 (Glenn Watson) wrote…

It seems like if there is a property animation occurring, this would never get the new transform, since this branch would be skipped by the glyph_keys.is_empty() check above. I guess we need to detect if this has changed since we last calculated the glyph keys to invalidate them.

I made it remember the glyph transform and then compare that for invalidation.


webrender/src/glyph_rasterizer/mod.rs, line 142 at r1 (raw file):

Previously, gw3583 (Glenn Watson) wrote…

Should these be epsilon checks due to float precision?

Modified these to use approx_eq


Comments from Reviewable

@lsalzman
Copy link
Contributor Author

lsalzman commented May 23, 2018

Okay, fixed the mentioned issues. Gecko try run was already done. Only some things I need to fuzz on the Gecko-side, but otherwise everything is okay.


Review status: 24 of 28 files reviewed at latest revision, 2 unresolved discussions.


Comments from Reviewable

@gw3583
Copy link
Collaborator

gw3583 commented May 24, 2018

Looks like there are some CI failures (I haven't looked into them).

It would be good to paste the try run results here, so that we know which tests are awaiting annotation updates while landing other PRs until the next WR update.

@lsalzman
Copy link
Contributor Author

lsalzman commented May 24, 2018

It looks like the failing test is wrench rawtest which does not fail locally at all. The rawtest failure hypothetically seems unrelated to this, but I am not sure exactly what rawtest does?

@gw3583
Copy link
Collaborator

gw3583 commented May 24, 2018

This is the rawtest failure:

hidpi factor: 1 (native 1)
Shader override path: None
	hit testing test...
	retained blob images test...
thread 'main' panicked at 'assertion failed: called.load(Ordering::SeqCst) == 1', wrench/src/rawtest.rs:261:9
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at libstd/sys_common/backtrace.rs:71
             at libstd/sys_common/backtrace.rs:59
   2: std::panicking::default_hook::{{closure}}
             at libstd/panicking.rs:207
   3: std::panicking::default_hook
             at libstd/panicking.rs:223
   4: std::panicking::rust_panic_with_hook
             at libstd/panicking.rs:402
   5: <unknown>
             at /checkout/src/libstd/panicking.rs:365
Traceback (most recent call last):
  File "script/headless.py", line 75, in <module>
    subprocess.check_call(['../target/release/wrench', '--no-scissor', '-h'] + sys.argv[1:])
  File "/usr/lib/python2.7/subprocess.py", line 541, in check_call
    raise CalledProcessError(retcode, cmd)

I ran your branch locally on my machine, which also does not fail locally. @jrmuizel any ideas what the cause of this might be?

@lsalzman lsalzman force-pushed the lsalzman:no-scroll-subpixel branch from 9e8f464 to 58ccf52 May 24, 2018
@lsalzman
Copy link
Contributor Author

lsalzman commented May 24, 2018

Did a rebase and pushed again, and the rawtest didn't fail this time. Maybe that blob image rawtest is one of our first intermittent failures? :(

Either way, I don't think I am necessarily the cause of it.

@lsalzman
Copy link
Contributor Author

lsalzman commented May 24, 2018

Jeff made PR #2775 to hopefully catch the intermittent sooner so it can be investigated.

@gw3583
Copy link
Collaborator

gw3583 commented May 24, 2018

@bors-servo r+ 🚀

@bors-servo
Copy link
Contributor

bors-servo commented May 24, 2018

📌 Commit 58ccf52 has been approved by gw3583

@bors-servo
Copy link
Contributor

bors-servo commented May 24, 2018

Testing commit 58ccf52 with merge 4b92630...

bors-servo added a commit that referenced this pull request May 24, 2018
round off subpixel offsets from scroll origins

This is to address downstream Gecko bug https://bugzilla.mozilla.org/show_bug.cgi?id=1458921

The problem is that when both the glyphs and the transform contain a subpixel offset, they can combine together to produce visual artifacts. If you imagine two glyphs, one with offset 0.3, and one with offset, 0.2, if positioned at scroll offset 0, they both round to device pixel 0. If the scroll offset becomes 0.2, suddenly the 0.3 rounds up to device pixel 1, while the 0.2 glyph still rounds to 0. So this can contribute to the appearance of a wavefront of messed up hinting as you slowly scroll along.

We're not necessarily doing things differently in the text shader itself, as both Cairo and Skia round the position after the transform is applied. However, in Gecko, we ensure that never let Cairo or Skia see a subpixel offset from scrolling so this issue never arises, despite them handling rounding of glyph positioning at a similar time during rasterization.

Thus, if we just ensure the scroll offset is rounded up front like in Gecko, the problem disappears.

<!-- 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/2738)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented May 24, 2018

☀️ Test successful - status-appveyor, status-taskcluster
Approved by: gw3583
Pushing 4b92630 to master...

@bors-servo bors-servo merged commit 58ccf52 into servo:master May 24, 2018
3 of 4 checks passed
3 of 4 checks passed
code-review/reviewable 9 files, 2 discussions left (gw3583)
Details
Taskcluster (pull_request) TaskGroup: success
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
homu Test successful
Details
bors-servo added a commit that referenced this pull request Jun 19, 2018
snap the text run offset and glyph offsets separately

This fixes issues introduced by #2738. Since the transform offset was snapped separately from the text/glyph offsets, this led to problems where certain Servo reftests might migrate a subpixel offset between the transform and the text offset.

As a compromise, Glenn and I agreed it would be best to combine the transform and text offset for snapping, then separately base the subpixel positioning off of only the glyph offset. This both keeps the resolution of Gecko's scrolling issue while addressing the Servo issue.

<!-- 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/2830)
<!-- Reviewable:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

5 participants
You can’t perform that action at this time.