Open Bug 1634162 Opened 2 years ago Updated 5 months ago

3D transformed text blurry and unreadable with webrender


(Core :: Graphics: WebRender, defect, P3)




Tracking Status
firefox-esr68 --- unaffected
firefox-esr78 --- disabled
firefox75 --- wontfix
firefox76 --- wontfix
firefox77 --- wontfix
firefox78 --- wontfix
firefox79 --- fix-optional


(Reporter: bugs, Unassigned)




(Keywords: regression)


(1 file)

Had the silly idea of rendering 0.999… similar to the wikipedia svg, but instead of using progressively smaller character styling and separate divs, thought I'd just try positioning and transforming it.

Result in Firefox was hopelessly blurred for no obvious reason.

Chrome rendered it crisply, but oddly has another bug that Firefox has as well (just really hard to make out in firefox due to the blur) where the digits are swapped further down the line.

I made where the 9s are replaced with Es to make this more obvious.

I see the blur with webrender but not without. Are you using webrender?

Flags: needinfo?(bugs)
Component: Graphics → Graphics: WebRender
Summary: Firefox renders 3D transformed text blurry and unreadable. Chrome does not. → 3D transformed text blurry and unreadable with webrender

I am indeed! We were wondering about that in freenode #firefox (irc://, but yesterday was pretty busy so forgot to follow up in bug.

Flags: needinfo?(bugs)

I was getting it in Webrender on linux, Caspy7 in windows.
I assume that's just the blurriness and not the text flipping.

Attached image text_compare.png

Thanks nemo for the great description and repro steps! Also thanks Timothy for finding the regression range.

The digit flipping is just aliasing, there's not much we can do about that -- it's similar to the effect in movies where an accelerating car will have the wheels stop and then spin backwards.

The blurring is more serious. In this test case we create a surface that's 142000 x 200 pixels (depends on browser width + DPI setting). If we try to render this on the GPU directly we can only do 8000 pixels at most so there's a 17x magnification.

The old code that establishes a raster root doesn't have this problem.
Side note, non-Webrender is kind of halfway -- it blurs horizontally.

Going back to raster roots brings back the issues of 1. having to rasterize SWGL text with perspective, 2. applying filters in the wrong order, and 3. not being able to cache all pictures. I'm not sure where we are with #1 especially so I'll ping Lee and Glenn on what we could do about this.

Flags: needinfo?(lsalzman)
Flags: needinfo?(gwatson)
Flags: needinfo?(bpeers)

This is all speculation, I haven't looked at it in detail yet.

I think we can probably get acceptable quality in this case (the same or better than regular Gecko) while retaining the local-space rasterization, with some tweaks to how we draw it.

The first thing I'd look into is why we were looking to create a surface that is 142000 pixels wide - that seems wider than I'd expect, given the test case.

I'd also look into what size glyph we're selecting to rasterize the surface with, and whether that's based on any local scale suggestion by Gecko in the requested raster space, or if we're determining it automatically.

My guess to what's happening: we're selecting a large glyph size in pixels, trying to get good quality rendering for the first few glyphs. This has a knock-on effect of making the natural width of the surface extremely wide, which we then scale down a huge amount due to the surface requirements.

It seems possible that if this is occurring, we would get better quality in this case by selecting a smaller glyph size - I think this would give a similar result as normal Firefox.

Does that sound plausible?

Flags: needinfo?(gwatson)

(In reply to Bert Peers [:bpeers] from comment #5)

Side note, non-Webrender is kind of halfway -- it blurs horizontally.

FWIW, my non-wr rendering is much crisper than your screenshot (on mac). It's not as crisp as the establish render root case in your screenshot though.

Thanks for the comments.
Taking a quick look at a wr-capture, there's this font in the interners. Each 'E' takes up ~123 pixels horizontally and there's a 1000 of them. The source sets font-size to 10% of viewport width, which would be ~120 pixels (~1800 / 150%DPI * 10%) (I'm just guessing how layout works).

Would that mean we'll never have more than 8 pixels per E?
Is chopping up long text-runs in layout an idea?

common: (
    flags: (
        bits: 1,
    prim_rect: (
        x: 7.770833492279053,
        y: -10.208333015441895,
        w: 123220.5859375,
        h: 181,
font: (
    base: (
        instance_key: ((3), 4),
        font_key: ((3), 3),
        size: 9635,

// ...
glyphs: ([
        index: 40,
        point: (-7.770833492279053, 146.2083282470703),
    ),// [0]
        index: 40,
        point: (115.39582824707031, 146.2083282470703),
    ),// [1]
Severity: -- → S3
Priority: -- → P3

I tried to investigate this a little, and inside TextRunPrimitive::update_font_instance, it looks like the addition of root_scaling_factor is somewhat interacting badly with this testcase. The rotateY transform, as it becomes more extreme towards 45 degrees, seems to cause root_scaling_factor to go towards 0.01739... This then gets recorded in the FontTransform via glyph_raster_scale (which incorporates root_scaling_factor), which the font backend sees as a request to render the font at a very very small resolution (about 5px).

Given that originated in bug 1613260, and I am not sure exactly how root_scaling_factor is derived, Bert, is there anything we can do to mitigate that part?

Flags: needinfo?(lsalzman) → needinfo?(bpeers)

Thanks for confirming Lee.
The small font size is due to trying to fit a 140,000 pixel wide surface into a 8192-wide rendertarget.
The perspective transform is forcing the text into an off screen surface, and the 1000 glyphs at a large font size result in a large surface.
There is no obvious fix for this since everything is "visible" -- we cannot be smart and clip invisible chunks of text to get that 140000 number down.

The old code did not have this problem because it would rasterize directly into screen space, ie. the glyphs are rasterized directly with a perspective transform (AFAIK). This avoids the issue but then requires support for such rendering in SWGL. It would also be the wrong order of operations in some cases (eg. incorrectly blurring in screenspace after the perspective).

That's my understanding so far, I don't have a solution :\

Flags: needinfo?(bpeers)
See Also: → 1640523

BTW, I don't know if this adds any information, but this was the wikipedia SVG I was talking about:
off of

That I decided to try a quick CSS simulation of.
What's curious to me is that while the SVG seems to have a heck of a lot of digits (assuming each m in the paths is a new digit, 411+184*5 = 1331 ) it does render cleanly in firefox with webrender.
Admittedly the digits are paths and not actual font, but should that matter?

Anyway, figured I'd link it just in case it offers any context.

Er... 411+184*8 = 1883 - sorry. missed a few paths when counting 'em.

Minor update. Was supporting someone with a similar issue and linked to this testcase as a dramatic explanation.
I was surprised that the blurring at worsened. It used to be that this was a severely out of focus image where you could just barely make out the general shape of the digits. Now it is nothing but a series of blobs of uniform height, as if the transform is broken as well.

You need to log in before you can comment on or make changes to this bug.