Closed Bug 1348980 Opened 3 years ago Closed 3 years ago

Add Unscaled Font abstraction

Categories

(Core :: Graphics: Text, enhancement)

enhancement
Not set

Tracking

()

RESOLVED FIXED
mozilla55
Tracking Status
firefox55 --- fixed

People

(Reporter: jrmuizel, Assigned: lsalzman)

References

(Depends on 1 open bug)

Details

Attachments

(2 files, 4 obsolete files)

This adds an UnscaledFont abstraction to Moz2D. Having this in place allows us better to manage font data on the webrender side because we can distinguish between ScaledFonts that share the same underlying gfxFont.
Attached patch Add and use UnscaledFont (obsolete) — Splinter Review
This patch is by Lee. There's been general agreement about this approach from the graphics side and we probably have a lot of context that you may be missing. Please ask if you have any questions about what's going on here or why.
Attachment #8849281 - Flags: review?(jfkthame)
This currently doesn't build on linux because of it's use of FT_Reference_Face which is only available in Freetype 2.4.2. We're using 2.3.11 on the build machines.
Depends on: 1349285
One thing that will probably help make things cleaner will be the removal of the old Pango/FC config code see bug 1285533
Attached patch Add and use UnscaledFont (obsolete) — Splinter Review
Assignee: nobody → jmuizelaar
Attachment #8849281 - Attachment is obsolete: true
Attachment #8849281 - Flags: review?(jfkthame)
Just a quick synopsis of what is going on here with this work:

The current flow of font objects is:

gfxPlatformFontList holds gfxFontEntrys, which are essentially quasi-unscaled fonts. gfxFontEntry produces gfxFonts, which are sized and possibly re-styled instances. From there, ScaledFonts are transiently produced by gfxFonts to transport low-level platform font API objects into the Moz2D DrawTargets.

Because ScaledFont is transient/created-on-demand and loses information about underlying unscaled font resources, it is not terribly convenient for any form of identification/serialization, since multiple ScaledFonts may ultimately refer to the same underlying platform font API objects.

This can be a problem, for example, in our print-via-parent setup where we might ultimately serialize the same font multiple times just because we have no way of communicating the sharing of the underlying font resource. We have similar issues with serializing fonts across the process gap with WebRender.

Because there is a desire to ideally keep Moz2D able to build independent of thebes, we need an abstraction for representing this sharing within Moz2D. A further complication is that while gfxFontEntry is "sort of" like an unscaled font, it doesn't always have access to the final font resources that might only be known at the time the gfxFont is created/resolved. So a given gfxFontEntry may, in truth, produce many different underlying platform font API objects. Thus a hypothetical UnscaledFont has a many-to-one relationship to gfxFontEntry. gfxFont has a many-to-one relationship to UnscaledFont, as gfxFonts while they differ in size and style may still point to said shared UnscaledFont. Further motivating this is that ScaledFont and gfxFont have different lifetimes, so a gfxFont may potentially be destroyed while ScaledFonts still exist that point to the underlying font resource. 

To bridge all these concerns, UnscaledFont most naturally fits the bill, so that ScaledFonts and gfxFonts can share an UnscaledFont which can be freely passed around inside Moz2D, while also giving gfxFontEntry a way to distinguish and cache multiple different shared font resources that it might produce. Serialization of the actual font can then be done more naturally against the UnscaledFont, leaving ScaledFont to only deal with serialization of instance data.
Synopsis of the contents of this patch given in above comment.
Assignee: jmuizelaar → lsalzman
Attachment #8850139 - Attachment is obsolete: true
Status: NEW → ASSIGNED
Attachment #8851094 - Flags: review?(jfkthame)
I separated out the bits dealing with using UnscaledFont to create and delete WebRender font keys into this patch.
Attachment #8851095 - Flags: review?(jmuizelaar)
Attachment #8851095 - Flags: review?(jmuizelaar) → review+
Why do we need variation settings to be included in UnscaledFontMac? Offhand that seems counter-intuitive to me; multiple instances of a variation font all share the same underlying font resource. I'd expect variations to be parameters (like size) that are just applied when instantiating a scaled font from the underlying data.
Flags: needinfo?(lsalzman)
(In reply to Jonathan Kew (:jfkthame) from comment #8)
> Why do we need variation settings to be included in UnscaledFontMac? Offhand
> that seems counter-intuitive to me; multiple instances of a variation font
> all share the same underlying font resource. I'd expect variations to be
> parameters (like size) that are just applied when instantiating a scaled
> font from the underlying data.

The idea was that multiple scaled fonts will possibly still share variation settings, and variation settings occupy an uncanny valley of not quite a scaled font setting, not quite an unscaled font setting. Unless we have reason to believe they vary wildly and frequently, I thought it made more sense to associate them with unscaled fonts so they can be properly shared amongst ScaledFonts without thus needing to instantiate a new CG font every time.
Flags: needinfo?(lsalzman)
So I guess the question becomes: when we go to serialize a ScaledFontMac, are we better off to serialize those variation settings with each ScaledFontMac, or do variation settings so rarely vary that there is no harm in treating them as essentially part of the UnscaledFont data? By harm, I mean serialization bloat...
Per IRC discussion with Jonathan Kew, we decided that for now we will leave the variations stuff in ScaledFont, rather than moving it into UnscaledFont initially. Hence this patch update.

Once further work proceeds on variations, it can be decided if the cost necessitates moving them into UnscaledFont, but that should be at least easier to do with UnscaledFont in place.
Attachment #8851094 - Attachment is obsolete: true
Attachment #8851094 - Flags: review?(jfkthame)
Attachment #8854539 - Flags: review?(jfkthame)
Comment on attachment 8854539 [details] [diff] [review]
implement UnscaledFont API for Moz2D and thebes

Review of attachment 8854539 [details] [diff] [review]:
-----------------------------------------------------------------

This basically looks OK, but I think the unscaled font cache stuff in many of the classes can be simplified/removed; only the fontconfig implementation, AFAICS, needs to be prepared to deal with an arbitrary number of unscaled fonts in a single gfxFontEntry, so that machinery can live purely within that subclass, and the others can use much simpler strategies.

::: gfx/2d/2D.h
@@ +776,5 @@
>      return mUserData.Get(key);
>    }
>  
> +  void SetUnscaledFont(const RefPtr<UnscaledFont>& aUnscaledFont) {
> +    MOZ_ASSERT(!mUnscaledFont && aUnscaledFont);

So the assertion here means that mUnscaledFont can only be set once, and never changed. Would it be feasible to simply pass it to the ScaledFont constructor, rather than having a separate setter?

::: gfx/2d/UnscaledFontFreeType.h
@@ +60,5 @@
> +  uint32_t mIndex;
> +};
> +
> +#ifdef MOZ_WIDGET_GTK
> +class UnscaledFontFontconfig : public UnscaledFontFreeType

Why do we need this? It doesn't seem to add anything to UnscaledFontFreeType.

::: gfx/2d/UnscaledFontMac.h
@@ +44,5 @@
> +    bool operator==(const CacheKey& aOther)
> +    {
> +      return true;
> +    }
> +  };

This CacheKey doesn't look very useful! Can we either make it do something meaningful, or remove it altogether if it's not needed?

(IIUC, the assumption is that a MacOSFontEntry will only ever have a single associated UnscaledFontMac, so there's no need for the cache to distinguish between entries. But in that case, we don't need a multiple-entry cache there at all, just give it a single WeakPtr<UnscaledFontMac> or whatever to hold its single entry.)

::: gfx/thebes/gfxDWriteFontList.cpp
@@ +594,5 @@
> +            return nullptr;
> +        }
> +
> +        unscaledFont = new UnscaledFontDWrite(fontFace, sims);
> +        mUnscaledFontCache.Add(unscaledFont);

There are only two possible cache entries here: with sims=NONE and with sims=BOLD.

So why not just have an array mUnscaledFonts[2], with the two possible entries in fixed positions [0] and [1], and skip the Lookup/Add/MoveToFront stuff the gfxUnscaledFontCache is doing?

ISTM that gfxUnscaledFontCache<> is a solution designed for the more open-ended fontconfig case, where in principle we might be dealing with some unknown number of resources within a single gfxFontEntry, but is just overkill for DWrite or Mac fonts.

::: gfx/thebes/gfxFT2FontList.cpp
@@ +248,5 @@
> +            mFilename.IsEmpty() ?
> +                new UnscaledFontFreeType(mFTFace) :
> +                new UnscaledFontFreeType(mFilename.BeginReading(),
> +                                         mFTFontIndex);
> +        mUnscaledFontCache.Add(unscaledFont);

Again, is there any need for a multi-entry cache here? AFAICS, any given FT2FontEntry can only create a single UnscaledFontFreeType, based on its mFilename/mFTFontIndex/mFTFace fields, and once it has done that, the cache lookup will always find that single entry. So a single mUnscaledFont field should be all it needs.

::: gfx/thebes/gfxFcPlatformFontList.cpp
@@ +856,5 @@
> +    FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index);
> +
> +    RefPtr<UnscaledFontFontconfig> unscaledFont =
> +        mUnscaledFontCache.Lookup(
> +            UnscaledFontFontconfig::CacheKey(ToCharPtr(file), index));

Should we be doing this lookup (based on file & index) without checking whether we have mFontData, in which case we actually want an unscaled font based on mFTFace rather than on file & index at all?

(I guess it works, because in that case the file & index are empty/zero, and the lookup successfully finds an unscaled font created in that way. But that seems a bit obscure; it should at least be described in comments so it's clear why it is valid to do such a lookup.)

::: gfx/thebes/gfxFontEntry.h
@@ +97,5 @@
>      gfxCharacterMap& operator=(const gfxCharacterMap&);
>  };
>  
> +template<typename UnscaledFontT, size_t N = 3>
> +class gfxUnscaledFontCache {

I think this is only needed for the fontconfig case, so it doesn't need to be a template here, it can just be defined in gfxFcPlatformFontList where it's needed.

How about, instead of just caching the 3 most recent unscaled fonts for the entry, accumulate them in a (growable) nsTArray? Is it plausible that the number of distinct unscaled fonts generated by a single fontEntry would ever become larger than a handful or so, even in the fontconfig case?

::: gfx/thebes/gfxGDIFontList.cpp
@@ +250,5 @@
> +already_AddRefed<UnscaledFontGDI>
> +GDIFontEntry::LookupUnscaledFont(HFONT aFont)
> +{
> +    LOGFONT lf;
> +    if (aFont) {

Would it ever be valid/meaningful to call this with a null HFONT, or should we simply assert that aFont != nullptr? Proceeding with a zeroed-out LOGFONT doesn't look like a recipe for successful rendering...
(In reply to Jonathan Kew (:jfkthame) from comment #12)
> Comment on attachment 8854539 [details] [diff] [review]
> implement UnscaledFont API for Moz2D and thebes
> 
> Review of attachment 8854539 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> This basically looks OK, but I think the unscaled font cache stuff in many
> of the classes can be simplified/removed; only the fontconfig
> implementation, AFAICS, needs to be prepared to deal with an arbitrary
> number of unscaled fonts in a single gfxFontEntry, so that machinery can
> live purely within that subclass, and the others can use much simpler
> strategies.

I guess I can make it so.
 
> ::: gfx/2d/2D.h
> @@ +776,5 @@
> >      return mUserData.Get(key);
> >    }
> >  
> > +  void SetUnscaledFont(const RefPtr<UnscaledFont>& aUnscaledFont) {
> > +    MOZ_ASSERT(!mUnscaledFont && aUnscaledFont);
> 
> So the assertion here means that mUnscaledFont can only be set once, and
> never changed. Would it be feasible to simply pass it to the ScaledFont
> constructor, rather than having a separate setter?

The problem is there are so many different constructors within Moz2D Factory,
and adjusting all of them seemed a bit messy. I guess it could be done in principle,
but just a lot of messy extra plumbing to pass something through that will not even
be used in the code it is passed to. but if you would like it so, I suppose I can do
that as well.
 
> ::: gfx/2d/UnscaledFontFreeType.h
> @@ +60,5 @@
> > +  uint32_t mIndex;
> > +};
> > +
> > +#ifdef MOZ_WIDGET_GTK
> > +class UnscaledFontFontconfig : public UnscaledFontFreeType
> 
> Why do we need this? It doesn't seem to add anything to UnscaledFontFreeType.

Initially I did not have this, but later for serialization purposes it turns out
to be beneficial to preserve the actual type. Otherwise the type information gets
lost, and I need to serialize that out separately. This seemed ultimately cleaner
to me.

> ::: gfx/2d/UnscaledFontMac.h
> @@ +44,5 @@
> > +    bool operator==(const CacheKey& aOther)
> > +    {
> > +      return true;
> > +    }
> > +  };
> 
> This CacheKey doesn't look very useful! Can we either make it do something
> meaningful, or remove it altogether if it's not needed?
> 
> (IIUC, the assumption is that a MacOSFontEntry will only ever have a single
> associated UnscaledFontMac, so there's no need for the cache to distinguish
> between entries. But in that case, we don't need a multiple-entry cache
> there at all, just give it a single WeakPtr<UnscaledFontMac> or whatever to
> hold its single entry.)

This might make more sense now that variation settings are no longer in UnscaledFontMac, yes.

> ::: gfx/thebes/gfxDWriteFontList.cpp
> @@ +594,5 @@
> > +            return nullptr;
> > +        }
> > +
> > +        unscaledFont = new UnscaledFontDWrite(fontFace, sims);
> > +        mUnscaledFontCache.Add(unscaledFont);
> 
> There are only two possible cache entries here: with sims=NONE and with
> sims=BOLD.
> 
> So why not just have an array mUnscaledFonts[2], with the two possible
> entries in fixed positions [0] and [1], and skip the Lookup/Add/MoveToFront
> stuff the gfxUnscaledFontCache is doing?
> 
> ISTM that gfxUnscaledFontCache<> is a solution designed for the more
> open-ended fontconfig case, where in principle we might be dealing with some
> unknown number of resources within a single gfxFontEntry, but is just
> overkill for DWrite or Mac fonts.
> 
> ::: gfx/thebes/gfxFT2FontList.cpp
> @@ +248,5 @@
> > +            mFilename.IsEmpty() ?
> > +                new UnscaledFontFreeType(mFTFace) :
> > +                new UnscaledFontFreeType(mFilename.BeginReading(),
> > +                                         mFTFontIndex);
> > +        mUnscaledFontCache.Add(unscaledFont);
> 
> Again, is there any need for a multi-entry cache here? AFAICS, any given
> FT2FontEntry can only create a single UnscaledFontFreeType, based on its
> mFilename/mFTFontIndex/mFTFace fields, and once it has done that, the cache
> lookup will always find that single entry. So a single mUnscaledFont field
> should be all it needs.
> 
> ::: gfx/thebes/gfxFcPlatformFontList.cpp
> @@ +856,5 @@
> > +    FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index);
> > +
> > +    RefPtr<UnscaledFontFontconfig> unscaledFont =
> > +        mUnscaledFontCache.Lookup(
> > +            UnscaledFontFontconfig::CacheKey(ToCharPtr(file), index));
> 
> Should we be doing this lookup (based on file & index) without checking
> whether we have mFontData, in which case we actually want an unscaled font
> based on mFTFace rather than on file & index at all?
> 
> (I guess it works, because in that case the file & index are empty/zero, and
> the lookup successfully finds an unscaled font created in that way. But that
> seems a bit obscure; it should at least be described in comments so it's
> clear why it is valid to do such a lookup.)

I guess I could be more explicit here about the failure case, warning or asserting
if there is !mFontData but also no file or index.

> ::: gfx/thebes/gfxFontEntry.h
> @@ +97,5 @@
> >      gfxCharacterMap& operator=(const gfxCharacterMap&);
> >  };
> >  
> > +template<typename UnscaledFontT, size_t N = 3>
> > +class gfxUnscaledFontCache {
> 
> I think this is only needed for the fontconfig case, so it doesn't need to
> be a template here, it can just be defined in gfxFcPlatformFontList where
> it's needed.
> 
> How about, instead of just caching the 3 most recent unscaled fonts for the
> entry, accumulate them in a (growable) nsTArray? Is it plausible that the
> number of distinct unscaled fonts generated by a single fontEntry would ever
> become larger than a handful or so, even in the fontconfig case?

This would become increasingly expensive to search/insert/maintain as the number of combinations grew, which is part of why the move-to-front strategy is used there too. I thought the move-to-front cache was a nice way to keep all costs low while in the majority of cases always having the right unscaled font around.

> ::: gfx/thebes/gfxGDIFontList.cpp
> @@ +250,5 @@
> > +already_AddRefed<UnscaledFontGDI>
> > +GDIFontEntry::LookupUnscaledFont(HFONT aFont)
> > +{
> > +    LOGFONT lf;
> > +    if (aFont) {
> 
> Would it ever be valid/meaningful to call this with a null HFONT, or should
> we simply assert that aFont != nullptr? Proceeding with a zeroed-out LOGFONT
> doesn't look like a recipe for successful rendering...

It can be in failure cases, due to the way gfxGDIFont is constructed. An assertion here wouldn't help us in release, since it's going to march right past, wherein we need at least some unscaled font around temporarily. The only alternative would be to always force the gfxGDIFont to be invalid in that case, and guarantee it will be destructed without ever getting used, and thus allowing the unscaled font to be null in that case.
(In reply to Lee Salzman [:lsalzman] from comment #13)
> > ::: gfx/2d/2D.h
> > @@ +776,5 @@
> > >      return mUserData.Get(key);
> > >    }
> > >  
> > > +  void SetUnscaledFont(const RefPtr<UnscaledFont>& aUnscaledFont) {
> > > +    MOZ_ASSERT(!mUnscaledFont && aUnscaledFont);
> > 
> > So the assertion here means that mUnscaledFont can only be set once, and
> > never changed. Would it be feasible to simply pass it to the ScaledFont
> > constructor, rather than having a separate setter?
> 
> The problem is there are so many different constructors within Moz2D Factory,
> and adjusting all of them seemed a bit messy. I guess it could be done in
> principle,
> but just a lot of messy extra plumbing to pass something through that will
> not even
> be used in the code it is passed to. but if you would like it so, I suppose
> I can do
> that as well.

True, the constructor doesn't need it, in the sense that it won't do anything with the unscaled font. But IMO the scaled font's mUnscaledFont member is (or should be) an immutable reference to the underlying font resource, and as such, it makes most sense for it to be initialized up front, when the scaled font is created, and never changed thereafter; it shouldn't need a setter method because there's never any reason to modify it after creation.

So that would be my preference; but if it's too much trouble, I guess we can live with doing it separately after initial construction of the scaled font. That just seems less clean and more error-prone to me. (If it's a required argument to the constructor, every caller is forced to pass it. With a separate post-construction setter, it's possible to forget to set it at all.)


>  
> > ::: gfx/2d/UnscaledFontFreeType.h
> > @@ +60,5 @@
> > > +  uint32_t mIndex;
> > > +};
> > > +
> > > +#ifdef MOZ_WIDGET_GTK
> > > +class UnscaledFontFontconfig : public UnscaledFontFreeType
> > 
> > Why do we need this? It doesn't seem to add anything to UnscaledFontFreeType.
> 
> Initially I did not have this, but later for serialization purposes it turns
> out
> to be beneficial to preserve the actual type. Otherwise the type information
> gets
> lost, and I need to serialize that out separately. This seemed ultimately
> cleaner
> to me.

OK, fair enough.

> > ::: gfx/thebes/gfxFcPlatformFontList.cpp
> > @@ +856,5 @@
> > > +    FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index);
> > > +
> > > +    RefPtr<UnscaledFontFontconfig> unscaledFont =
> > > +        mUnscaledFontCache.Lookup(
> > > +            UnscaledFontFontconfig::CacheKey(ToCharPtr(file), index));
> > 
> > Should we be doing this lookup (based on file & index) without checking
> > whether we have mFontData, in which case we actually want an unscaled font
> > based on mFTFace rather than on file & index at all?
> > 
> > (I guess it works, because in that case the file & index are empty/zero, and
> > the lookup successfully finds an unscaled font created in that way. But that
> > seems a bit obscure; it should at least be described in comments so it's
> > clear why it is valid to do such a lookup.)
> 
> I guess I could be more explicit here about the failure case, warning or
> asserting
> if there is !mFontData but also no file or index.

Yes, asserting that would be good; and also comment that if file and index are null, the Lookup here will find an entry that was created for a font based on mFontData.

> > ::: gfx/thebes/gfxFontEntry.h
> > @@ +97,5 @@
> > >      gfxCharacterMap& operator=(const gfxCharacterMap&);
> > >  };
> > >  
> > > +template<typename UnscaledFontT, size_t N = 3>
> > > +class gfxUnscaledFontCache {
> > 
> > I think this is only needed for the fontconfig case, so it doesn't need to
> > be a template here, it can just be defined in gfxFcPlatformFontList where
> > it's needed.
> > 
> > How about, instead of just caching the 3 most recent unscaled fonts for the
> > entry, accumulate them in a (growable) nsTArray? Is it plausible that the
> > number of distinct unscaled fonts generated by a single fontEntry would ever
> > become larger than a handful or so, even in the fontconfig case?
> 
> This would become increasingly expensive to search/insert/maintain as the
> number of combinations grew, which is part of why the move-to-front strategy
> is used there too. I thought the move-to-front cache was a nice way to keep
> all costs low while in the majority of cases always having the right
> unscaled font around.

I was assuming the number of combinations will never grow large enough for the search to become significantly expensive, but maybe that's not true?

Anyhow, I'm OK with keeping this as is. I mostly wondered whether there'll be cases where, say, 4 different unscaled font resources get used, such that they're repeatedly evicted from the 3-item rotating cache in turn and then re-created, where a growable array would have been able to hold on to all of them.

> > ::: gfx/thebes/gfxGDIFontList.cpp
> > @@ +250,5 @@
> > > +already_AddRefed<UnscaledFontGDI>
> > > +GDIFontEntry::LookupUnscaledFont(HFONT aFont)
> > > +{
> > > +    LOGFONT lf;
> > > +    if (aFont) {
> > 
> > Would it ever be valid/meaningful to call this with a null HFONT, or should
> > we simply assert that aFont != nullptr? Proceeding with a zeroed-out LOGFONT
> > doesn't look like a recipe for successful rendering...
> 
> It can be in failure cases, due to the way gfxGDIFont is constructed.

AFAICS, gfxGDIFont always does "mFont = ::CreateFontIndirectW(&logFont);" during its Initialize(); if that fails (and returns NULL), I think we're in pretty bad shape (out of GDI resources?). Probably gfxGDIFont should check for that and set mIsValid = false if it happens, though I suspect that in practice we're about to die anyway...
This patch addresses the issues brought up in the previous:

1) adjusts all the ScaledFont constructors to accept an UnscaledFont, got rid of the setter, and fixes up Factory to require that information as well
2) removes the usage of gfxUnscaledFontCache for all font lists except for gfxFcPlatformFontList, and removed the unneeded CacheKey stuff from most of the associated UnscaledFonts
3) made gfxGDIFont no longer create an UnscaledFont when HFONT is invalid
4) gfxFontconfigFontEntry now more explicit about requiring file/index from pattern when and only when there is no font data
Attachment #8854539 - Attachment is obsolete: true
Attachment #8854539 - Flags: review?(jfkthame)
Attachment #8855185 - Flags: review?(jfkthame)
Comment on attachment 8855185 [details] [diff] [review]
implement UnscaledFont API for Moz2D and thebes

Review of attachment 8855185 [details] [diff] [review]:
-----------------------------------------------------------------

Thanks, I like this much better. Just a couple further suggestions; r=me with these fixed up.

::: gfx/2d/UnscaledFontFreeType.h
@@ +51,5 @@
> +    const char* mFile;
> +    uint32_t mIndex;
> +  };
> +
> +  CacheKey GetCacheKey() const { return CacheKey(mFile.c_str(), mIndex); }

Please move the CacheKey stuff into the fontconfig subclass, as that's the only case where it is used. (So the data members below will need to become `protected` rather than `private`.)

::: gfx/thebes/gfxFontEntry.h
@@ +97,5 @@
>      gfxCharacterMap& operator=(const gfxCharacterMap&);
>  };
>  
> +template<typename UnscaledFontT, size_t N = 3>
> +class gfxUnscaledFontCache {

Let's remove this from the generic gfxFontEntry.h header, and make it a nested class inside gfxFontconfigFontEntry, as that's the only place it is used.

(And it doesn't need to be a template at all. Just plain 'class UnscaledFontCache' working directly with UnscaledFontFontconfig should be fine.)
Attachment #8855185 - Flags: review?(jfkthame) → review+
Pushed by lsalzman@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/42dc8787a005
implement UnscaledFont API for Moz2D and thebes. r=jfkthame
https://hg.mozilla.org/integration/mozilla-inbound/rev/b475e054bbd1
use UnscaledFont to track WebRender font keys. r=jrmuizel
https://hg.mozilla.org/mozilla-central/rev/42dc8787a005
https://hg.mozilla.org/mozilla-central/rev/b475e054bbd1
Status: ASSIGNED → RESOLVED
Closed: 3 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla55
Blocks: 1355931
You need to log in before you can comment on or make changes to this bug.