Bug 1393055 - map ScaledFonts to WebRender FontInstanceKeys. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Wed, 30 Aug 2017 13:45:11 -0400
changeset 378127 a47911a010ff4d9757d6edec1aaf78e78f9dc443
parent 378126 14fcefb89e4fe5b6101549ed32f7ab25d6d73fd5
child 378128 4cf028133a2eaca159b1c6523033eba09be06480
push id32422
push userarchaeopteryx@coole-files.de
push dateFri, 01 Sep 2017 08:39:53 +0000
treeherdermozilla-central@a3585c77e2b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1393055
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1393055 - map ScaledFonts to WebRender FontInstanceKeys. r=jrmuizel MozReview-Commit-ID: AadEI5CnTZ1
gfx/2d/2D.h
gfx/2d/ScaledFontBase.cpp
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderMessageUtils.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/WebRenderTypes.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/moz2d_renderer.rs
gfx/webrender_bindings/webrender_ffi_generated.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -69,31 +69,41 @@ struct CGContext;
 typedef struct CGContext *CGContextRef;
 
 namespace mozilla {
 
 class Mutex;
 
 namespace gfx {
 class UnscaledFont;
+class ScaledFont;
 }
 
 template<>
 struct WeakPtrTraits<gfx::UnscaledFont>
 {
   static void AssertSafeToAccessFromNonOwningThread()
   {
     // We want to allow UnscaledFont objects that were created on the main
     // thread to be accessed from other threads if the Servo font metrics
     // mutex is locked, and for objects created on Servo style worker threads
     // to be accessed later back on the main thread.
     AssertIsMainThreadOrServoFontMetricsLocked();
   }
 };
 
+template<>
+struct WeakPtrTraits<gfx::ScaledFont>
+{
+  static void AssertSafeToAccessFromNonOwningThread()
+  {
+    AssertIsMainThreadOrServoFontMetricsLocked();
+  }
+};
+
 namespace gfx {
 
 class ScaledFont;
 class SourceSurface;
 class DataSourceSurface;
 class DrawTarget;
 class DrawEventRecorder;
 class FilterNode;
@@ -764,33 +774,39 @@ public:
   {
     return nullptr;
   }
 
 protected:
   UnscaledFont() {}
 
 private:
-  static uint32_t sDeletionCounter;
+  static Atomic<uint32_t> sDeletionCounter;
 };
 
 /** This class is an abstraction of a backend/platform specific font object
  * at a particular size. It is passed into text drawing calls to describe
  * the font used for the drawing call.
  */
-class ScaledFont : public external::AtomicRefCounted<ScaledFont>
+class ScaledFont
+  : public external::AtomicRefCounted<ScaledFont>
+  , public SupportsWeakPtr<ScaledFont>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)
-  virtual ~ScaledFont() {}
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ScaledFont)
+
+  virtual ~ScaledFont();
 
   virtual FontType GetType() const = 0;
   virtual Float GetSize() const = 0;
   virtual AntialiasMode GetDefaultAAMode();
 
+  static uint32_t DeletionCounter() { return sDeletionCounter; }
+
   /** This allows getting a path that describes the outline of a set of glyphs.
    * A target is passed in so that the guarantee is made the returned path
    * can be used with any DrawTarget that has the same backend as the one
    * passed in.
    */
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
 
   /** This copies the path describing the glyphs into a PathBuilder. We use this
@@ -828,16 +844,19 @@ public:
 
 protected:
   explicit ScaledFont(const RefPtr<UnscaledFont>& aUnscaledFont)
     : mUnscaledFont(aUnscaledFont)
   {}
 
   UserData mUserData;
   RefPtr<UnscaledFont> mUnscaledFont;
+
+private:
+  static Atomic<uint32_t> sDeletionCounter;
 };
 
 /**
  * Derived classes hold a native font resource from which to create
  * ScaledFonts.
  */
 class NativeFontResource : public external::AtomicRefCounted<NativeFontResource>
 {
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -21,23 +21,30 @@
 #include <vector>
 #include <cmath>
 
 using namespace std;
 
 namespace mozilla {
 namespace gfx {
 
-uint32_t UnscaledFont::sDeletionCounter = 0;
+Atomic<uint32_t> UnscaledFont::sDeletionCounter(0);
 
 UnscaledFont::~UnscaledFont()
 {
   sDeletionCounter++;
 }
 
+Atomic<uint32_t> ScaledFont::sDeletionCounter(0);
+
+ScaledFont::~ScaledFont()
+{
+  sDeletionCounter++;
+}
+
 AntialiasMode
 ScaledFont::GetDefaultAAMode()
 {
   if (gfxPrefs::DisableAllTextAA()) {
     return AntialiasMode::NONE;
   }
 
   return AntialiasMode::DEFAULT;
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -17,16 +17,19 @@ include protocol PTexture;
 using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::FontInstanceKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeFontInstanceOptions from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeFontInstancePlatformOptions from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
 using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
 
 namespace mozilla {
 namespace layers {
 
@@ -49,16 +52,19 @@ parent:
   async AddBlobImage(ImageKey aImageKey, IntSize aSize, uint32_t aStride,
                      SurfaceFormat aFormat, ByteBuffer aBytes);
   async UpdateImage(ImageKey aImageKey, IntSize aSize,
                    SurfaceFormat aFormat, ByteBuffer aBytes);
   async DeleteImage(ImageKey aImageKey);
   async DeleteCompositorAnimations(uint64_t[] aIds);
   async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex);
   async DeleteFont(FontKey aFontKey);
+  async AddFontInstance(FontInstanceKey aInstanceKey, FontKey aFontKey, float aGlyphSize,
+                        MaybeFontInstanceOptions aOptions, MaybeFontInstancePlatformOptions aPlatformOptions);
+  async DeleteFontInstance(FontInstanceKey aInstanceKey);
   async DPBegin(IntSize aSize);
   async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
               LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc,
               WebRenderScrollData aScrollData, IdNamespace aIdNamespace, TimeStamp txnStartTime, TimeStamp fwdTime);
   sync DPSyncEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
                  LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc,
                  WebRenderScrollData aScrollData, IdNamespace aIdNamespace, TimeStamp txnStartTime, TimeStamp fwdTime);
   async ParentCommands(WebRenderParentCommand[] commands);
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -24,16 +24,17 @@ WebRenderBridgeChild::WebRenderBridgeChi
   , mIsInTransaction(false)
   , mIsInClearCachedResources(false)
   , mIdNamespace{0}
   , mResourceId(0)
   , mPipelineId(aPipelineId)
   , mIPCOpen(false)
   , mDestroyed(false)
   , mFontKeysDeleted(0)
+  , mFontInstanceKeysDeleted(0)
 {
 }
 
 void
 WebRenderBridgeChild::Destroy(bool aIsSync)
 {
   if (!IPCOpen()) {
     return;
@@ -214,73 +215,92 @@ WriteFontFileData(const uint8_t* aData, 
 void
 WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
                                  gfx::ScaledFont* aFont, const gfx::Color& aColor, const StackingContextHelper& aSc,
                                  const LayerRect& aBounds, const LayerRect& aClip)
 {
   MOZ_ASSERT(aFont);
   MOZ_ASSERT(!aGlyphs.IsEmpty());
 
-  wr::WrFontKey key = GetFontKeyForScaledFont(aFont);
+  wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
   MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
 
   nsTArray<wr::GlyphInstance> wr_glyph_instances;
   wr_glyph_instances.SetLength(aGlyphs.Length());
 
   for (size_t j = 0; j < aGlyphs.Length(); j++) {
     wr_glyph_instances[j].index = aGlyphs[j].mIndex;
     wr_glyph_instances[j].point = aSc.ToRelativeLayoutPoint(
             LayerPoint::FromUnknownPoint(aGlyphs[j].mPosition));
   }
 
   aBuilder.PushText(aSc.ToRelativeLayoutRect(aBounds),
                     aSc.ToRelativeLayoutRect(aClip),
                     aColor,
                     key,
-                    Range<const wr::GlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()),
-                    aFont->GetSize());
+                    Range<const wr::GlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()));
 }
 
-wr::FontKey
+wr::FontInstanceKey
 WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aScaledFont);
   MOZ_ASSERT((aScaledFont->GetType() == gfx::FontType::DWRITE) ||
              (aScaledFont->GetType() == gfx::FontType::MAC) ||
              (aScaledFont->GetType() == gfx::FontType::FONTCONFIG));
 
+  wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 };
+  if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) {
+    return instanceKey;
+  }
+
   RefPtr<gfx::UnscaledFont> unscaled = aScaledFont->GetUnscaledFont();
   MOZ_ASSERT(unscaled);
 
-  wr::FontKey key = { wr::IdNamespace { 0 }, 0};
-  if (mFontKeys.Get(unscaled, &key)) {
-    return key;
+  wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0};
+  if (!mFontKeys.Get(unscaled, &fontKey)) {
+    FontFileData data;
+    if (!unscaled->GetFontFileData(WriteFontFileData, &data) ||
+        !data.mFontBuffer.mData) {
+      return instanceKey;
+    }
+
+    fontKey.mNamespace = GetNamespace();
+    fontKey.mHandle = GetNextResourceId();
+
+    SendAddRawFont(fontKey, data.mFontBuffer, data.mFontIndex);
+
+    mFontKeys.Put(unscaled, fontKey);
   }
 
-  FontFileData data;
-  if (!unscaled->GetFontFileData(WriteFontFileData, &data) ||
-      !data.mFontBuffer.mData) {
-    return key;
-  }
+  instanceKey.mNamespace = GetNamespace();
+  instanceKey.mHandle = GetNextResourceId();
 
-  key.mNamespace = GetNamespace();
-  key.mHandle = GetNextResourceId();
+  SendAddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(), Nothing(), Nothing());
 
-  SendAddRawFont(key, data.mFontBuffer, data.mFontIndex);
+  mFontInstanceKeys.Put(aScaledFont, instanceKey);
 
-  mFontKeys.Put(unscaled, key);
-
-  return key;
+  return instanceKey;
 }
 
 void
 WebRenderBridgeChild::RemoveExpiredFontKeys()
 {
-  uint32_t counter = gfx::UnscaledFont::DeletionCounter();
+  uint32_t counter = gfx::ScaledFont::DeletionCounter();
+  if (mFontInstanceKeysDeleted != counter) {
+    mFontInstanceKeysDeleted = counter;
+    for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) {
+      if (!iter.Key()) {
+        SendDeleteFontInstance(iter.Data());
+        iter.Remove();
+      }
+    }
+  }
+  counter = gfx::UnscaledFont::DeletionCounter();
   if (mFontKeysDeleted != counter) {
     mFontKeysDeleted = counter;
     for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) {
       if (!iter.Key()) {
         SendDeleteFont(iter.Data());
         iter.Remove();
       }
     }
@@ -454,17 +474,18 @@ WebRenderBridgeChild::InForwarderThread(
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeChild::RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace)
 {
   // Update mIdNamespace to identify obsolete keys and messages by WebRenderBridgeParent.
   // Since usage of invalid keys could cause crash in webrender.
   mIdNamespace = aNewIdNamespace;
-  // Just clear FontKeys, they are removed during WebRenderAPI destruction.
+  // Just clear FontInstaceKeys/FontKeys, they are removed during WebRenderAPI destruction.
+  mFontInstanceKeys.Clear();
   mFontKeys.Clear();
   GetCompositorBridgeChild()->RecvInvalidateLayers(wr::AsUint64(mPipelineId));
   return IPC_OK();
 }
 
 void
 WebRenderBridgeChild::BeginClearCachedResources()
 {
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -22,38 +22,42 @@ class DisplayListBuilder;
 
 namespace layers {
 
 class CompositableClient;
 class CompositorBridgeChild;
 class StackingContextHelper;
 class TextureForwarder;
 
-class UnscaledFontHashKey : public PLDHashEntryHdr
+template<class T>
+class WeakPtrHashKey : public PLDHashEntryHdr
 {
 public:
-  typedef gfx::UnscaledFont* KeyType;
-  typedef const gfx::UnscaledFont* KeyTypePointer;
+  typedef T* KeyType;
+  typedef const T* KeyTypePointer;
 
-  explicit UnscaledFontHashKey(KeyTypePointer aKey) : mKey(const_cast<KeyType>(aKey)) {}
+  explicit WeakPtrHashKey(KeyTypePointer aKey) : mKey(const_cast<KeyType>(aKey)) {}
 
   KeyType GetKey() const { return mKey; }
   bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
 
   static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
   static PLDHashNumber HashKey(KeyTypePointer aKey)
   {
     return NS_PTR_TO_UINT32(aKey) >> 2;
   }
   enum { ALLOW_MEMMOVE = true };
 
 private:
-  WeakPtr<gfx::UnscaledFont> mKey;
+  WeakPtr<T> mKey;
 };
 
+typedef WeakPtrHashKey<gfx::UnscaledFont> UnscaledFontHashKey;
+typedef WeakPtrHashKey<gfx::ScaledFont> ScaledFontHashKey;
+
 class WebRenderBridgeChild final : public PWebRenderBridgeChild
                                  , public CompositableForwarder
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override)
 
 public:
   explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
 
@@ -104,17 +108,17 @@ public:
     return wr::WrImageKey{ GetNamespace(), GetNextResourceId() };
   }
 
   void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
                   gfx::ScaledFont* aFont, const gfx::Color& aColor,
                   const StackingContextHelper& aSc,
                   const LayerRect& aBounds, const LayerRect& aClip);
 
-  wr::FontKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
+  wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
 
   void RemoveExpiredFontKeys();
   void ClearReadLocks();
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
 
 private:
@@ -174,14 +178,17 @@ private:
   uint32_t mResourceId;
   wr::PipelineId mPipelineId;
 
   bool mIPCOpen;
   bool mDestroyed;
 
   uint32_t mFontKeysDeleted;
   nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mFontKeys;
+
+  uint32_t mFontInstanceKeysDeleted;
+  nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mFontInstanceKeys;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_WebRenderBridgeChild_h
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -307,27 +307,74 @@ WebRenderBridgeParent::RecvDeleteFont(co
   }
   MOZ_ASSERT(mApi);
 
   // Check if key is obsoleted.
   if (aFontKey.mNamespace != mIdNamespace) {
     return IPC_OK();
   }
 
-  if (mFontKeys.find(wr::AsUint64(aFontKey)) != mFontKeys.end()) {
-    mFontKeys.erase(wr::AsUint64(aFontKey));
+  if (mFontKeys.erase(wr::AsUint64(aFontKey)) > 0) {
     mApi->DeleteFont(aFontKey);
   } else {
     MOZ_ASSERT_UNREACHABLE("invalid FontKey");
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvAddFontInstance(const wr::FontInstanceKey& aInstanceKey,
+                                           const wr::FontKey& aFontKey,
+                                           const float& aGlyphSize,
+                                           const MaybeFontInstanceOptions& aOptions,
+                                           const MaybeFontInstancePlatformOptions& aPlatformOptions)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+
+  // Check if key is obsoleted.
+  if (aInstanceKey.mNamespace != mIdNamespace) {
+    return IPC_OK();
+  }
+
+  MOZ_ASSERT(mApi);
+  MOZ_ASSERT(mFontInstanceKeys.find(wr::AsUint64(aInstanceKey)) == mFontInstanceKeys.end());
+
+  mFontInstanceKeys.insert(wr::AsUint64(aInstanceKey));
+  mApi->AddFontInstance(aInstanceKey, aFontKey, aGlyphSize,
+                        aOptions.ptrOr(nullptr), aPlatformOptions.ptrOr(nullptr));
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvDeleteFontInstance(const wr::FontInstanceKey& aInstanceKey)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mApi);
+
+  // Check if key is obsoleted.
+  if (aInstanceKey.mNamespace != mIdNamespace) {
+    return IPC_OK();
+  }
+
+  if (mFontInstanceKeys.erase(wr::AsUint64(aInstanceKey)) > 0) {
+    mApi->DeleteFontInstance(aInstanceKey);
+  } else {
+    MOZ_ASSERT_UNREACHABLE("invalid FontInstanceKey");
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey,
                                        const gfx::IntSize& aSize,
                                        const gfx::SurfaceFormat& aFormat,
                                        const ByteBuffer& aBuffer)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
@@ -352,36 +399,34 @@ WebRenderBridgeParent::RecvDeleteImage(c
   }
   MOZ_ASSERT(mApi);
 
   // Check if key is obsoleted.
   if (aImageKey.mNamespace != mIdNamespace) {
     return IPC_OK();
   }
 
-  if (mActiveImageKeys.find(wr::AsUint64(aImageKey)) != mActiveImageKeys.end()) {
-    mActiveImageKeys.erase(wr::AsUint64(aImageKey));
+  if (mActiveImageKeys.erase(wr::AsUint64(aImageKey)) > 0) {
     mKeysToDelete.push_back(aImageKey);
   } else {
     MOZ_ASSERT_UNREACHABLE("invalid ImageKey");
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
 
   for (uint32_t i = 0; i < aIds.Length(); i++) {
-    if (mActiveAnimations.find(aIds[i]) != mActiveAnimations.end()) {
+    if (mActiveAnimations.erase(aIds[i]) > 0) {
       mAnimStorage->ClearById(aIds[i]);
-      mActiveAnimations.erase(aIds[i]);
     } else {
       NS_ERROR("Tried to delete invalid animation");
     }
   }
 
   return IPC_OK();
 }
 
@@ -1302,16 +1347,17 @@ WebRenderBridgeParent::ClearResources()
     return;
   }
 
   uint32_t wrEpoch = GetNextWrEpoch();
   mApi->ClearRootDisplayList(wr::NewEpoch(wrEpoch), mPipelineId);
   // Schedule composition to clean up Pipeline
   mCompositorScheduler->ScheduleComposition();
   // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction.
+  mFontInstanceKeys.clear();
   mFontKeys.clear();
   mActiveImageKeys.clear();
   mKeysToDelete.clear();
   for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
     iter.Data()->ClearWrBridge();
   }
   mExternalImageIds.Clear();
   for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -87,16 +87,22 @@ public:
                                           const gfx::SurfaceFormat& aFormat,
                                           const ByteBuffer& aBuffer) override;
   mozilla::ipc::IPCResult RecvDeleteImage(const wr::ImageKey& a1) override;
   mozilla::ipc::IPCResult RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds) override;
   mozilla::ipc::IPCResult RecvAddRawFont(const wr::FontKey& aFontKey,
                                          const ByteBuffer& aBuffer,
                                          const uint32_t& aFontIndex) override;
   mozilla::ipc::IPCResult RecvDeleteFont(const wr::FontKey& aFontKey) override;
+  mozilla::ipc::IPCResult RecvAddFontInstance(const wr::FontInstanceKey& aInstanceKey,
+                                              const wr::FontKey& aFontKey,
+                                              const float& aGlyphSize,
+                                              const MaybeFontInstanceOptions& aOptions,
+                                              const MaybeFontInstancePlatformOptions& aPlatformOptions) override;
+  mozilla::ipc::IPCResult RecvDeleteFontInstance(const wr::FontInstanceKey& aInstanceKey) override;
   mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize) override;
   mozilla::ipc::IPCResult RecvDPEnd(const gfx::IntSize& aSize,
                                     InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                     InfallibleTArray<OpDestroy>&& aToDestroy,
                                     const uint64_t& aFwdTransactionId,
                                     const uint64_t& aTransactionId,
                                     const wr::LayoutSize& aContentSize,
                                     const wr::ByteBuffer& dl,
@@ -267,16 +273,17 @@ private:
   RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   RefPtr<CompositorAnimationStorage> mAnimStorage;
   std::vector<wr::ImageKey> mKeysToDelete;
   // mActiveImageKeys and mFontKeys are used to avoid leaking animations when
   // WebRenderBridgeParent is destroyed abnormally and Tab move between different windows.
   std::unordered_set<uint64_t> mActiveImageKeys;
   std::unordered_set<uint64_t> mFontKeys;
+  std::unordered_set<uint64_t> mFontInstanceKeys;
   // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is
   // destroyed abnormally and Tab move between different windows.
   std::unordered_set<uint64_t> mActiveAnimations;
   nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mAsyncCompositables;
   nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mExternalImageIds;
 
   TimeStamp mPreviousFrameTimeStamp;
   // These fields keep track of the latest layer observer epoch values in the child and the
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -49,16 +49,34 @@ struct ParamTraits<mozilla::wr::ImageKey
 
 template<>
 struct ParamTraits<mozilla::wr::FontKey>
   : public PlainOldDataSerializer<mozilla::wr::FontKey>
 {
 };
 
 template<>
+struct ParamTraits<mozilla::wr::FontInstanceKey>
+  : public PlainOldDataSerializer<mozilla::wr::FontInstanceKey>
+{
+};
+
+template<>
+struct ParamTraits<mozilla::wr::FontInstanceOptions>
+  : public PlainOldDataSerializer<mozilla::wr::FontInstanceOptions>
+{
+};
+
+template<>
+struct ParamTraits<mozilla::wr::FontInstancePlatformOptions>
+  : public PlainOldDataSerializer<mozilla::wr::FontInstancePlatformOptions>
+{
+};
+
+template<>
 struct ParamTraits<mozilla::wr::ExternalImageId>
   : public PlainOldDataSerializer<mozilla::wr::ExternalImageId>
 {
 };
 
 template<>
 struct ParamTraits<mozilla::wr::PipelineId>
   : public PlainOldDataSerializer<mozilla::wr::PipelineId>
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -519,16 +519,32 @@ WebRenderAPI::AddRawFont(wr::FontKey aKe
 }
 
 void
 WebRenderAPI::DeleteFont(wr::FontKey aKey)
 {
   wr_api_delete_font(mDocHandle, aKey);
 }
 
+void
+WebRenderAPI::AddFontInstance(wr::FontInstanceKey aKey,
+                              wr::FontKey aFontKey,
+                              float aGlyphSize,
+                              const wr::FontInstanceOptions* aOptions,
+                              const wr::FontInstancePlatformOptions* aPlatformOptions)
+{
+  wr_api_add_font_instance(mDocHandle, aKey, aFontKey, aGlyphSize, aOptions, aPlatformOptions);
+}
+
+void
+WebRenderAPI::DeleteFontInstance(wr::FontInstanceKey aKey)
+{
+  wr_api_delete_font_instance(mDocHandle, aKey);
+}
+
 class FrameStartTime : public RendererEvent
 {
 public:
   explicit FrameStartTime(const TimeStamp& aTime)
     : mTime(aTime)
   {
     MOZ_COUNT_CTOR(FrameStartTime);
   }
@@ -917,25 +933,25 @@ DisplayListBuilder::PushBorderRadialGrad
     aRadius, aStops.Elements(), aStops.Length(),
     aExtendMode, aOutset);
 }
 
 void
 DisplayListBuilder::PushText(const wr::LayoutRect& aBounds,
                              const wr::LayoutRect& aClip,
                              const gfx::Color& aColor,
-                             wr::FontKey aFontKey,
+                             wr::FontInstanceKey aFontKey,
                              Range<const wr::GlyphInstance> aGlyphBuffer,
-                             float aGlyphSize)
+                             const wr::GlyphOptions* aGlyphOptions)
 {
   wr_dp_push_text(mWrState, aBounds, aClip,
                   ToColorF(aColor),
                   aFontKey,
                   &aGlyphBuffer[0], aGlyphBuffer.length(),
-                  aGlyphSize);
+                  aGlyphOptions);
 }
 
 void
 DisplayListBuilder::PushLine(const wr::LayoutRect& aClip,
                              const wr::Line& aLine)
 {
  wr_dp_push_line(mWrState, aClip, aLine.baseline, aLine.start, aLine.end,
                  aLine.orientation, aLine.width, aLine.color, aLine.style);
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -118,16 +118,24 @@ public:
                            uint8_t aChannelIndex = 0);
 
   void DeleteImage(wr::ImageKey aKey);
 
   void AddRawFont(wr::FontKey aKey, Range<uint8_t> aBytes, uint32_t aIndex);
 
   void DeleteFont(wr::FontKey aKey);
 
+  void AddFontInstance(wr::FontInstanceKey aKey,
+                       wr::FontKey aFontKey,
+                       float aGlyphSize,
+                       const wr::FontInstanceOptions* aOptions,
+                       const wr::FontInstancePlatformOptions* aPlatformOptions);
+
+  void DeleteFontInstance(wr::FontInstanceKey aKey);
+
   void SetFrameStartTime(const TimeStamp& aTime);
 
   void RunOnRenderThread(UniquePtr<RendererEvent> aEvent);
   void Readback(gfx::IntSize aSize, uint8_t *aBuffer, uint32_t aBufferSize);
 
   void Pause();
   bool Resume();
 
@@ -295,19 +303,19 @@ public:
                                 const wr::LayoutSize& aRadius,
                                 const nsTArray<wr::GradientStop>& aStops,
                                 wr::ExtendMode aExtendMode,
                                 const wr::SideOffsets2D_f32& aOutset);
 
   void PushText(const wr::LayoutRect& aBounds,
                 const wr::LayoutRect& aClip,
                 const gfx::Color& aColor,
-                wr::FontKey aFontKey,
+                wr::FontInstanceKey aFontKey,
                 Range<const wr::GlyphInstance> aGlyphBuffer,
-                float aGlyphSize);
+                const wr::GlyphOptions* aGlyphOptions = nullptr);
 
   void PushLine(const wr::LayoutRect& aClip,
                 const wr::Line& aLine);
 
   void PushTextShadow(const wr::LayoutRect& aBounds,
                       const wr::LayoutRect& aClip,
                       const wr::TextShadow& aShadow);
 
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -22,23 +22,27 @@
 
 namespace mozilla {
 namespace wr {
 
 typedef wr::WrWindowId WindowId;
 typedef wr::WrPipelineId PipelineId;
 typedef wr::WrImageKey ImageKey;
 typedef wr::WrFontKey FontKey;
+typedef wr::WrFontInstanceKey FontInstanceKey;
 typedef wr::WrEpoch Epoch;
 typedef wr::WrExternalImageId ExternalImageId;
 typedef wr::WrDebugFlags DebugFlags;
 
 typedef mozilla::Maybe<mozilla::wr::WrImageMask> MaybeImageMask;
 typedef Maybe<ExternalImageId> MaybeExternalImageId;
 
+typedef Maybe<FontInstanceOptions> MaybeFontInstanceOptions;
+typedef Maybe<FontInstancePlatformOptions> MaybeFontInstancePlatformOptions;
+
 inline WindowId NewWindowId(uint64_t aId) {
   WindowId id;
   id.mHandle = aId;
   return id;
 }
 
 inline Epoch NewEpoch(uint32_t aEpoch) {
   Epoch e;
@@ -136,16 +140,29 @@ inline uint64_t AsUint64(const FontKey& 
 
 inline FontKey AsFontKey(const uint64_t& aId) {
   FontKey fontKey;
   fontKey.mNamespace.mHandle = aId >> 32;
   fontKey.mHandle = aId;
   return fontKey;
 }
 
+// Whenever possible, use wr::FontInstanceKey instead of manipulating uint64_t.
+inline uint64_t AsUint64(const FontInstanceKey& aId) {
+  return (static_cast<uint64_t>(aId.mNamespace.mHandle) << 32)
+        + static_cast<uint64_t>(aId.mHandle);
+}
+
+inline FontInstanceKey AsFontInstanceKey(const uint64_t& aId) {
+  FontInstanceKey instanceKey;
+  instanceKey.mNamespace.mHandle = aId >> 32;
+  instanceKey.mHandle = aId;
+  return instanceKey;
+}
+
 // Whenever possible, use wr::PipelineId instead of manipulating uint64_t.
 inline uint64_t AsUint64(const PipelineId& aId) {
   return (static_cast<uint64_t>(aId.mNamespace) << 32)
         + static_cast<uint64_t>(aId.mHandle);
 }
 
 inline PipelineId AsPipelineId(const uint64_t& aId) {
   PipelineId pipeline;
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -34,16 +34,18 @@ type WrIdNamespace = IdNamespace;
 
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrPipelineId = PipelineId;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrImageKey = ImageKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrFontKey = FontKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
+type WrFontInstanceKey = FontInstanceKey;
+/// cbindgen:field-names=[mNamespace, mHandle]
 type WrYuvColorSpace = YuvColorSpace;
 
 fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
     if ptr.is_null() {
         &[]
     } else {
         unsafe { slice::from_raw_parts(ptr, len) }
     }
@@ -903,16 +905,42 @@ pub extern "C" fn wr_api_delete_font(dh:
                                      key: WrFontKey) {
     assert!(unsafe { is_in_compositor_thread() });
     let mut resources = ResourceUpdates::new();
     resources.delete_font(key);
     dh.api.update_resources(resources);
 }
 
 #[no_mangle]
+pub extern "C" fn wr_api_add_font_instance(dh: &mut DocumentHandle,
+                                           key: WrFontInstanceKey,
+                                           font_key: WrFontKey,
+                                           glyph_size: f32,
+                                           options: *const FontInstanceOptions,
+                                           platform_options: *const FontInstancePlatformOptions) {
+    assert!(unsafe { is_in_compositor_thread() });
+    let mut resources = ResourceUpdates::new();
+    resources.add_font_instance(key,
+                                font_key,
+                                Au::from_f32_px(glyph_size),
+                                unsafe { options.as_ref().cloned() },
+                                unsafe { platform_options.as_ref().cloned() });
+    dh.api.update_resources(resources);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_delete_font_instance(dh: &mut DocumentHandle,
+                                              key: WrFontInstanceKey) {
+    assert!(unsafe { is_in_compositor_thread() });
+    let mut resources = ResourceUpdates::new();
+    resources.delete_font_instance(key);
+    dh.api.update_resources(resources);
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn wr_api_get_namespace(dh: &mut DocumentHandle) -> WrIdNamespace {
     dh.api.get_namespace_id()
 }
 
 // RenderThread WIP notes:
 // In order to separate the compositor thread (or ipc receiver) and the render
 // thread, some of the logic below needs to be rewritten. In particular
 // the WrWindowState and Notifier implementations aren't designed to work with
@@ -1259,36 +1287,32 @@ pub extern "C" fn wr_dp_push_yuv_interle
                          image_rendering);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_text(state: &mut WrState,
                                   bounds: LayoutRect,
                                   clip: LayoutRect,
                                   color: ColorF,
-                                  font_key: WrFontKey,
+                                  font_key: WrFontInstanceKey,
                                   glyphs: *const GlyphInstance,
                                   glyph_count: u32,
-                                  glyph_size: f32) {
+                                  glyph_options: *const GlyphOptions) {
     assert!(unsafe { is_in_main_thread() });
 
     let glyph_slice = make_slice(glyphs, glyph_count as usize);
 
-    let colorf = ColorF::new(color.r, color.g, color.b, color.a);
-
-    let glyph_options = None; // TODO
     state.frame_builder
          .dl_builder
          .push_text(bounds,
                     Some(LocalClip::Rect(clip.into())),
                     &glyph_slice,
                     font_key,
-                    colorf,
-                    Au::from_f32_px(glyph_size),
-                    glyph_options);
+                    color,
+                    unsafe { glyph_options.as_ref().cloned() });
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_text_shadow(state: &mut WrState,
                                          bounds: LayoutRect,
                                          clip: LayoutRect,
                                          shadow: TextShadow) {
     assert!(unsafe { is_in_main_thread() });
--- a/gfx/webrender_bindings/src/moz2d_renderer.rs
+++ b/gfx/webrender_bindings/src/moz2d_renderer.rs
@@ -115,18 +115,22 @@ impl BlobImageRenderer for Moz2dImageRen
                 return result
             }
             self.rendered_images.insert(req, Some(result));
         }
 
         // If we break out of the loop above it means the channel closed unexpectedly.
         Err(BlobImageError::Other("Channel closed".into()))
     }
+
     fn delete_font(&mut self, _font: FontKey) {
     }
+
+    fn delete_font_instance(&mut self, _key: FontInstanceKey) {
+    }
 }
 
 impl Moz2dImageRenderer {
     pub fn new(workers: Arc<ThreadPool>) -> Self {
         let (tx, rx) = channel();
         Moz2dImageRenderer {
             blob_commands: HashMap::new(),
             rendered_images: HashMap::new(),
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -54,16 +54,24 @@ enum class ExternalImageType : uint32_t 
   Texture2DArrayHandle = 1,
   TextureRectHandle = 2,
   TextureExternalHandle = 3,
   ExternalBuffer = 4,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
+enum class FontRenderMode : uint32_t {
+  Mono = 0,
+  Alpha = 1,
+  Subpixel = 2,
+
+  Sentinel /* this must be last for serialization purposes. */
+};
+
 enum class ImageFormat : uint32_t {
   Invalid = 0,
   A8 = 1,
   RGB8 = 2,
   BGRA8 = 3,
   RGBAF32 = 4,
   RG8 = 5,
 
@@ -239,28 +247,58 @@ struct WrExternalImageId {
 
   bool operator==(const WrExternalImageId& aOther) const {
     return mHandle == aOther.mHandle;
   }
 };
 
 typedef ExternalImageType WrExternalImageBufferType;
 
+struct FontInstanceKey {
+  IdNamespace mNamespace;
+  uint32_t mHandle;
+
+  bool operator==(const FontInstanceKey& aOther) const {
+    return mNamespace == aOther.mNamespace &&
+           mHandle == aOther.mHandle;
+  }
+};
+
+typedef FontInstanceKey WrFontInstanceKey;
+
 struct FontKey {
   IdNamespace mNamespace;
   uint32_t mHandle;
 
   bool operator==(const FontKey& aOther) const {
     return mNamespace == aOther.mNamespace &&
            mHandle == aOther.mHandle;
   }
 };
 
 typedef FontKey WrFontKey;
 
+struct FontInstanceOptions {
+  FontRenderMode render_mode;
+
+  bool operator==(const FontInstanceOptions& aOther) const {
+    return render_mode == aOther.render_mode;
+  }
+};
+
+struct FontInstancePlatformOptions {
+  bool use_embedded_bitmap;
+  bool force_gdi_rendering;
+
+  bool operator==(const FontInstancePlatformOptions& aOther) const {
+    return use_embedded_bitmap == aOther.use_embedded_bitmap &&
+           force_gdi_rendering == aOther.force_gdi_rendering;
+  }
+};
+
 struct Epoch {
   uint32_t mHandle;
 
   bool operator==(const Epoch& aOther) const {
     return mHandle == aOther.mHandle;
   }
   bool operator<(const Epoch& aOther) const {
     return mHandle < aOther.mHandle;
@@ -587,16 +625,24 @@ struct GlyphInstance {
   LayoutPoint point;
 
   bool operator==(const GlyphInstance& aOther) const {
     return index == aOther.index &&
            point == aOther.point;
   }
 };
 
+struct GlyphOptions {
+  FontRenderMode render_mode;
+
+  bool operator==(const GlyphOptions& aOther) const {
+    return render_mode == aOther.render_mode;
+  }
+};
+
 struct TextShadow {
   LayoutVector2D offset;
   ColorF color;
   float blur_radius;
 
   bool operator==(const TextShadow& aOther) const {
     return offset == aOther.offset &&
            color == aOther.color &&
@@ -723,16 +769,25 @@ void wr_api_add_external_image(DocumentH
                                WrImageKey aImageKey,
                                const WrImageDescriptor *aDescriptor,
                                WrExternalImageId aExternalImageId,
                                WrExternalImageBufferType aBufferType,
                                uint8_t aChannelIndex)
 WR_FUNC;
 
 WR_INLINE
+void wr_api_add_font_instance(DocumentHandle *aDh,
+                              WrFontInstanceKey aKey,
+                              WrFontKey aFontKey,
+                              float aGlyphSize,
+                              const FontInstanceOptions *aOptions,
+                              const FontInstancePlatformOptions *aPlatformOptions)
+WR_FUNC;
+
+WR_INLINE
 void wr_api_add_image(DocumentHandle *aDh,
                       WrImageKey aImageKey,
                       const WrImageDescriptor *aDescriptor,
                       ByteSlice aBytes)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_add_raw_font(DocumentHandle *aDh,
@@ -758,16 +813,21 @@ void wr_api_delete(DocumentHandle *aDh)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 void wr_api_delete_font(DocumentHandle *aDh,
                         WrFontKey aKey)
 WR_FUNC;
 
 WR_INLINE
+void wr_api_delete_font_instance(DocumentHandle *aDh,
+                                 WrFontInstanceKey aKey)
+WR_FUNC;
+
+WR_INLINE
 void wr_api_delete_image(DocumentHandle *aDh,
                          WrImageKey aKey)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_finalize_builder(WrState *aState,
                              LayoutSize *aContentSize,
                              BuiltDisplayListDescriptor *aDlDescriptor,
@@ -1045,20 +1105,20 @@ void wr_dp_push_stacking_context(WrState
                                  size_t aFilterCount)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_text(WrState *aState,
                      LayoutRect aBounds,
                      LayoutRect aClip,
                      ColorF aColor,
-                     WrFontKey aFontKey,
+                     WrFontInstanceKey aFontKey,
                      const GlyphInstance *aGlyphs,
                      uint32_t aGlyphCount,
-                     float aGlyphSize)
+                     const GlyphOptions *aGlyphOptions)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_text_shadow(WrState *aState,
                             LayoutRect aBounds,
                             LayoutRect aClip,
                             TextShadow aShadow)
 WR_FUNC;