8220231: Cache HarfBuzz face object for same font's text layout calls
authordbatrak
Thu, 11 Apr 2019 10:49:36 -0700
changeset 54590 98473958d49a
parent 54509 6c291f12969f
child 54591 dc6c5c53669b
8220231: Cache HarfBuzz face object for same font's text layout calls Reviewed-by: prr, avu, serb
src/java.desktop/macosx/classes/sun/font/CFont.java
src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h
src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m
src/java.desktop/share/classes/sun/font/Font2D.java
src/java.desktop/share/classes/sun/font/FontScaler.java
src/java.desktop/share/classes/sun/font/FreetypeFontScaler.java
src/java.desktop/share/classes/sun/font/NullFontScaler.java
src/java.desktop/share/classes/sun/font/SunLayoutEngine.java
src/java.desktop/share/classes/sun/font/TrueTypeFont.java
src/java.desktop/share/native/common/font/fontscalerdefs.h
src/java.desktop/share/native/libfontmanager/HBShaper.c
src/java.desktop/share/native/libfontmanager/freetypeScaler.c
src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc
src/java.desktop/share/native/libfontmanager/hb-jdk.h
src/java.desktop/share/native/libfontmanager/sunFont.c
test/jdk/java/awt/font/TextLayout/FontLayoutStressTest.java
--- a/src/java.desktop/macosx/classes/sun/font/CFont.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/macosx/classes/sun/font/CFont.java	Thu Apr 11 10:49:36 2019 -0700
@@ -78,17 +78,10 @@
     }
 
     @Override
-    protected long getLayoutTableCache() {
-        return getLayoutTableCacheNative(getNativeFontPtr());
-    }
-
-    @Override
     protected byte[] getTableBytes(int tag) {
         return getTableBytesNative(getNativeFontPtr(), tag);
     }
 
-    private native synchronized long getLayoutTableCacheNative(long nativeFontPtr);
-
     private native byte[] getTableBytesNative(long nativeFontPtr, int tag);
 
     private static native long createNativeFont(final String nativeFontName,
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h	Thu Apr 11 10:49:36 2019 -0700
@@ -26,8 +26,6 @@
 #import <Cocoa/Cocoa.h>
 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
 
-#import "fontscalerdefs.h"
-
 #define MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE 256
 
 @interface AWTFont : NSObject {
@@ -35,7 +33,6 @@
     NSFont    *fFont;
     CGFontRef  fNativeCGFont;
     BOOL       fIsFakeItalic;
-    TTLayoutTableCache* layoutTableCache;
 }
 
 + (AWTFont *) awtFontForName:(NSString *)name
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m	Thu Apr 11 10:49:36 2019 -0700
@@ -42,33 +42,10 @@
     if (self) {
         fFont = [font retain];
         fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL);
-        layoutTableCache = NULL;
     }
     return self;
 }
 
-static TTLayoutTableCache* newCFontLayoutTableCache() {
-  TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache));
-  if (ltc) {
-    int i;
-    for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
-      ltc->entries[i].len = -1;
-    }
-  }
-  return ltc;
-}
-
-static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
-  if (ltc) {
-    int i;
-    for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
-      if(ltc->entries[i].ptr) free (ltc->entries[i].ptr);
-    }
-    if (ltc->kernPairs) free(ltc->kernPairs);
-    free(ltc);
-  }
-}
-
 - (void) dealloc {
     [fFont release];
     fFont = nil;
@@ -76,10 +53,6 @@
     if (fNativeCGFont) {
         CGFontRelease(fNativeCGFont);
     fNativeCGFont = NULL;
-    if (layoutTableCache != NULL) {
-        freeCFontLayoutTableCache(layoutTableCache);
-        layoutTableCache = NULL;
-    }
     }
 
     [super dealloc];
@@ -90,10 +63,6 @@
         CGFontRelease(fNativeCGFont);
     fNativeCGFont = NULL;
     }
-    if (layoutTableCache != NULL) {
-        freeCFontLayoutTableCache(layoutTableCache);
-        layoutTableCache = NULL;
-    }
     [super finalize];
 }
 
@@ -433,23 +402,6 @@
 
 /*
  * Class:     sun_font_CFont
- * Method:    getLayoutTableCacheNative
- * Signature: (J)J
- */
-JNIEXPORT jlong JNICALL
-Java_sun_font_CFont_getLayoutTableCacheNative
-    (JNIEnv *env, jclass clazz,
-     jlong awtFontPtr)
-{
-    AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
-    if (awtFont->layoutTableCache == NULL) {
-        awtFont->layoutTableCache = newCFontLayoutTableCache();
-    }
-    return (jlong)(awtFont->layoutTableCache);
-}
-
-/*
- * Class:     sun_font_CFont
  * Method:    getTableBytesNative
  * Signature: (JI)[B
  */
--- a/src/java.desktop/share/classes/sun/font/Font2D.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/classes/sun/font/Font2D.java	Thu Apr 11 10:49:36 2019 -0700
@@ -465,13 +465,6 @@
         return null;
     }
 
-    /* implemented for fonts backed by an sfnt that has
-     * OpenType or AAT layout tables.
-     */
-    protected long getLayoutTableCache() {
-        return 0L;
-    }
-
     /* Used only on OS X.
      */
     protected long getPlatformNativeFontPtr() {
--- a/src/java.desktop/share/classes/sun/font/FontScaler.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/classes/sun/font/FontScaler.java	Thu Apr 11 10:49:36 2019 -0700
@@ -181,25 +181,6 @@
     abstract int getMissingGlyphCode() throws FontScalerException;
     abstract int getGlyphCode(char charCode) throws FontScalerException;
 
-    /* This method returns table cache used by native layout engine.
-     * This cache is essentially just small collection of
-     * pointers to various truetype tables. See definition of TTLayoutTableCache
-     * in the fontscalerdefs.h for more details.
-     *
-     * Note that tables themselves have same format as defined in the truetype
-     * specification, i.e. font scaler do not need to perform any preprocessing.
-     *
-     * Probably it is better to have API to request pointers to each table
-     * separately instead of requesting pointer to some native structure.
-     * (then there is not need to share its definition by different
-     * implementations of scaler).
-     * However, this means multiple JNI calls and potential impact on performance.
-     *
-     * Note: return value 0 is legal.
-     *   This means tables are not available (e.g. type1 font).
-     */
-    abstract long getLayoutTableCache() throws FontScalerException;
-
     /* Used by the OpenType engine for mark positioning. */
     abstract Point2D.Float getGlyphPoint(long pScalerContext,
                                 int glyphCode, int ptNumber)
--- a/src/java.desktop/share/classes/sun/font/FreetypeFontScaler.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/classes/sun/font/FreetypeFontScaler.java	Thu Apr 11 10:49:36 2019 -0700
@@ -163,10 +163,6 @@
             .getNullScaler().getGlyphVectorOutline(0L, glyphs, numGlyphs, x, y);
     }
 
-    synchronized long getLayoutTableCache() throws FontScalerException {
-        return getLayoutTableCacheNative(nativeScaler);
-    }
-
     public synchronized void dispose() {
         if (nativeScaler != 0L) {
             disposeNativeScaler(font.get(), nativeScaler);
@@ -243,8 +239,6 @@
     native Point2D.Float getGlyphPointNative(Font2D font,
             long pScalerContext, long pScaler, int glyphCode, int ptNumber);
 
-    private native long getLayoutTableCacheNative(long pScaler);
-
     private native void disposeNativeScaler(Font2D font2D, long pScaler);
 
     private native int getGlyphCodeNative(Font2D font, long pScaler, char charCode);
--- a/src/java.desktop/share/classes/sun/font/NullFontScaler.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/classes/sun/font/NullFontScaler.java	Thu Apr 11 10:49:36 2019 -0700
@@ -64,8 +64,6 @@
         return new GeneralPath();
     }
 
-    long getLayoutTableCache() {return 0L;}
-
     long createScalerContext(double[] matrix, int aa,
         int fm, float boldness, float italic, boolean disableHinting) {
         return getNullScalerContext();
--- a/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java	Thu Apr 11 10:49:36 2019 -0700
@@ -31,10 +31,12 @@
 package sun.font;
 
 import sun.font.GlyphLayout.*;
+import sun.java2d.Disposer;
+import sun.java2d.DisposerRecord;
+
 import java.awt.geom.Point2D;
 import java.lang.ref.SoftReference;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.Locale;
 import java.util.WeakHashMap;
 
 /*
@@ -150,8 +152,10 @@
     }
 
     static WeakHashMap<Font2D, Boolean> aatInfo = new WeakHashMap<>();
+    private static final WeakHashMap<Font2D, FaceRef> facePtr =
+            new WeakHashMap<>();
 
-    private boolean isAAT(Font2D font) {
+    private static boolean isAAT(Font2D font) {
        Boolean aatObj;
        synchronized (aatInfo) {
            aatObj = aatInfo.get(font);
@@ -175,30 +179,67 @@
        return aat;
     }
 
+    private long getFacePtr(Font2D font2D) {
+        FaceRef ref;
+        synchronized (facePtr) {
+            ref = facePtr.computeIfAbsent(font2D, FaceRef::new);
+        }
+        return ref.getNativePtr();
+    }
+
     public void layout(FontStrikeDesc desc, float[] mat, float ptSize, int gmask,
                        int baseIndex, TextRecord tr, int typo_flags,
                        Point2D.Float pt, GVData data) {
         Font2D font = key.font();
         FontStrike strike = font.getStrike(desc);
-        long layoutTables = font.getLayoutTableCache();
         long pNativeFont = font.getPlatformNativeFontPtr(); // used on OSX
-        // pScaler probably not needed long term.
-        long pScaler = 0L;
-        if (font instanceof FileFont) {
-            pScaler = ((FileFont)font).getScaler().nativeScaler;
+        long pFace = getFacePtr(font);
+        if (pFace != 0) {
+            shape(font, strike, ptSize, mat, pNativeFont,
+                    pFace, isAAT(font),
+                    tr.text, data, key.script(),
+                    tr.start, tr.limit, baseIndex, pt,
+                    typo_flags, gmask);
         }
-        shape(font, strike, ptSize, mat, pScaler, pNativeFont,
-              layoutTables, isAAT(font),
-              tr.text, data, key.script(),
-              tr.start, tr.limit, baseIndex, pt,
-              typo_flags, gmask);
     }
 
     /* Native method to invoke harfbuzz layout engine */
     private static native boolean
         shape(Font2D font, FontStrike strike, float ptSize, float[] mat,
-              long pscaler, long pNativeFont, long layoutTables, boolean aat,
+              long pNativeFont, long pFace, boolean aat,
               char[] chars, GVData data,
               int script, int offset, int limit,
               int baseIndex, Point2D.Float pt, int typo_flags, int slot);
+
+    private static native long createFace(Font2D font,
+                                          boolean aat,
+                                          long platformNativeFontPtr);
+
+    private static native void disposeFace(long facePtr);
+
+    private static class FaceRef implements DisposerRecord {
+        private Font2D font;
+        private Long facePtr;
+
+        private FaceRef(Font2D font) {
+            this.font = font;
+        }
+
+        private synchronized long getNativePtr() {
+            if (facePtr == null) {
+                facePtr = createFace(font, isAAT(font),
+                        font.getPlatformNativeFontPtr());
+                if (facePtr != 0) {
+                    Disposer.addObjectRecord(font, this);
+                }
+                font = null;
+            }
+            return facePtr;
+        }
+
+        @Override
+        public void dispose() {
+            disposeFace(facePtr);
+        }
+    }
 }
--- a/src/java.desktop/share/classes/sun/font/TrueTypeFont.java	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/classes/sun/font/TrueTypeFont.java	Thu Apr 11 10:49:36 2019 -0700
@@ -897,15 +897,6 @@
     }
 
     @Override
-    protected long getLayoutTableCache() {
-        try {
-          return getScaler().getLayoutTableCache();
-        } catch(FontScalerException fe) {
-            return 0L;
-        }
-    }
-
-    @Override
     protected byte[] getTableBytes(int tag) {
         ByteBuffer buffer = getTableBuffer(tag);
         if (buffer == null) {
--- a/src/java.desktop/share/native/common/font/fontscalerdefs.h	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/native/common/font/fontscalerdefs.h	Thu Apr 11 10:49:36 2019 -0700
@@ -88,32 +88,8 @@
    */
 #define INVISIBLE_GLYPHS 0xfffe
 
-#define GSUB_TAG 0x47535542 /* 'GSUB' */
-#define GPOS_TAG 0x47504F53 /* 'GPOS' */
-#define GDEF_TAG 0x47444546 /* 'GDEF' */
-#define HEAD_TAG 0x68656164 /* 'head' */
-#define MORT_TAG 0x6D6F7274 /* 'mort' */
-#define MORX_TAG 0x6D6F7278 /* 'morx' */
-#define KERN_TAG 0x6B65726E /* 'kern' */
-
-typedef struct TTLayoutTableCacheEntry {
-  const void* ptr;
-  int   len;
-  int   tag;
-} TTLayoutTableCacheEntry;
-
-#define LAYOUTCACHE_ENTRIES 7
-
-typedef struct TTLayoutTableCache {
-  TTLayoutTableCacheEntry entries[LAYOUTCACHE_ENTRIES];
-  void* kernPairs;
-} TTLayoutTableCache;
-
 #include "sunfontids.h"
 
-JNIEXPORT extern TTLayoutTableCache* newLayoutTableCache();
-JNIEXPORT extern void freeLayoutTableCache(TTLayoutTableCache* ltc);
-
 /* If font is malformed then scaler context created by particular scaler
  * will be replaced by null scaler context.
  * Note that this context is not compatible with structure of the context
--- a/src/java.desktop/share/native/libfontmanager/HBShaper.c	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/native/libfontmanager/HBShaper.c	Thu Apr 11 10:49:36 2019 -0700
@@ -201,9 +201,7 @@
                        jobject font2D,
                        jobject fontStrike,
                        jfloat ptSize,
-                       jlong pScaler,
                        jlong pNativeFont,
-                       jlong layoutTables,
                        jfloatArray matrix,
                        jboolean aat) {
 
@@ -216,7 +214,6 @@
     fi->font2D = font2D;
     fi->fontStrike = fontStrike;
     fi->nativeFont = pNativeFont;
-    fi->layoutTables = (TTLayoutTableCache*)layoutTables;
     fi->aat = aat;
     (*env)->GetFloatArrayRegion(env, matrix, 0, 4, fi->matrix);
     fi->ptSize = ptSize;
@@ -241,9 +238,8 @@
      jobject fontStrike,
      jfloat ptSize,
      jfloatArray matrix,
-     jlong pScaler,
      jlong pNativeFont,
-     jlong layoutTables,
+     jlong pFace,
      jboolean aat,
      jcharArray text,
      jobject gvdata,
@@ -256,6 +252,7 @@
      jint slot) {
 
      hb_buffer_t *buffer;
+     hb_face_t* hbface;
      hb_font_t* hbfont;
      jchar  *chars;
      jsize len;
@@ -272,7 +269,7 @@
 
      JDKFontInfo *jdkFontInfo =
          createJDKFontInfo(env, font2D, fontStrike, ptSize,
-                           pScaler, pNativeFont, layoutTables, matrix, aat);
+                           pNativeFont, matrix, aat);
      if (!jdkFontInfo) {
         return JNI_FALSE;
      }
@@ -280,7 +277,8 @@
      jdkFontInfo->font2D = font2D;
      jdkFontInfo->fontStrike = fontStrike;
 
-     hbfont = hb_jdk_font_create(jdkFontInfo, NULL);
+     hbface = (hb_face_t*) jlong_to_ptr(pFace);
+     hbfont = hb_jdk_font_create(hbface, jdkFontInfo, NULL);
 
      buffer = hb_buffer_create();
      hb_buffer_set_script(buffer, getHBScriptCode(script));
--- a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c	Thu Apr 11 10:49:36 2019 -0700
@@ -69,7 +69,6 @@
     unsigned fontDataOffset;
     unsigned fontDataLength;
     unsigned fileSize;
-    TTLayoutTableCache* layoutTables;
 } FTScalerInfo;
 
 typedef struct FTScalerContext {
@@ -251,7 +250,6 @@
     if (type == TYPE1_FROM_JAVA) { /* TYPE1 */
         scalerInfo->fontData = (unsigned char*) malloc(filesize);
         scalerInfo->directBuffer = NULL;
-        scalerInfo->layoutTables = NULL;
         scalerInfo->fontDataLength = filesize;
 
         if (scalerInfo->fontData != NULL) {
@@ -866,32 +864,6 @@
     return ptr_to_jlong(glyphInfo);
 }
 
-
-/*
- * Class:     sun_font_FreetypeFontScaler
- * Method:    getLayoutTableCacheNative
- * Signature: (J)J
- */
-JNIEXPORT jlong JNICALL
-Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
-        JNIEnv *env, jobject scaler, jlong pScaler) {
-    FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
-
-    if (scalerInfo == NULL) {
-        invalidateJavaScaler(env, scaler, scalerInfo);
-        return 0L;
-    }
-
-    // init layout table cache in font
-    // we're assuming the font is a file font and moreover it is Truetype font
-    // otherwise we shouldn't be able to get here...
-    if (scalerInfo->layoutTables == NULL) {
-        scalerInfo->layoutTables = newLayoutTableCache();
-    }
-
-    return ptr_to_jlong(scalerInfo->layoutTables);
-}
-
 /*
  * Class:     sun_font_FreetypeFontScaler
  * Method:    disposeNativeScaler
--- a/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc	Thu Apr 11 10:49:36 2019 -0700
@@ -23,6 +23,9 @@
  * questions.
  */
 
+#include "jlong.h"
+#include "sun_font_SunLayoutEngine.h"
+
 #include "hb.h"
 #include "hb-jdk.h"
 #ifdef MACOSX
@@ -304,79 +307,113 @@
 static void _free_nothing(void*) {
 }
 
+struct Font2DPtr {
+    JavaVM* vmPtr;
+    jweak font2DRef;
+};
+
+static void cleanupFontInfo(void* data) {
+  Font2DPtr* fontInfo;
+  JNIEnv* env;
+
+  fontInfo = (Font2DPtr*) data;
+  fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1);
+  env->DeleteWeakGlobalRef(fontInfo->font2DRef);
+  free(data);
+}
+
 static hb_blob_t *
 reference_table(hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) {
 
-  JDKFontInfo *jdkFontInfo = (JDKFontInfo*)user_data;
-  JNIEnv* env = jdkFontInfo->env;
-  jobject font2D = jdkFontInfo->font2D;
-  jsize length = 0;
-  void* buffer = NULL;
-  int cacheIdx = 0;
+  Font2DPtr *fontInfo;
+  JNIEnv* env;
+  jobject font2D;
+  jsize length;
+  void* buffer;
 
   // HB_TAG_NONE is 0 and is used to get the whole font file.
   // It is not expected to be needed for JDK.
-  if (tag == 0 || jdkFontInfo->layoutTables == NULL) {
+  if (tag == 0) {
       return NULL;
   }
 
-  for (cacheIdx=0; cacheIdx<LAYOUTCACHE_ENTRIES; cacheIdx++) {
-    if (tag == jdkFontInfo->layoutTables->entries[cacheIdx].tag) break;
+  fontInfo = (Font2DPtr*)user_data;
+  fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1);
+  if (env == NULL) {
+    return NULL;
   }
-
-  if (cacheIdx < LAYOUTCACHE_ENTRIES) { // if found
-      if (jdkFontInfo->layoutTables->entries[cacheIdx].len != -1) {
-          length = jdkFontInfo->layoutTables->entries[cacheIdx].len;
-          buffer = (void*)jdkFontInfo->layoutTables->entries[cacheIdx].ptr;
-      }
-  }
+  font2D = fontInfo->font2DRef;
 
-  if (buffer == NULL) {
-      jbyteArray tableBytes = (jbyteArray)
-         env->CallObjectMethod(font2D, sunFontIDs.getTableBytesMID, tag);
-      if (tableBytes == NULL) {
-          return NULL;
-      }
-      length = env->GetArrayLength(tableBytes);
-      buffer = calloc(length, sizeof(jbyte));
-      env->GetByteArrayRegion(tableBytes, 0, length, (jbyte*)buffer);
-
-     if (cacheIdx >= LAYOUTCACHE_ENTRIES) { // not a cacheable table
-          return hb_blob_create((const char *)buffer, length,
-                                 HB_MEMORY_MODE_WRITABLE,
-                                 buffer, free);
-      } else {
-        jdkFontInfo->layoutTables->entries[cacheIdx].len = length;
-        jdkFontInfo->layoutTables->entries[cacheIdx].ptr = buffer;
-      }
+  jbyteArray tableBytes = (jbyteArray)
+     env->CallObjectMethod(font2D, sunFontIDs.getTableBytesMID, tag);
+  if (tableBytes == NULL) {
+      return NULL;
   }
+  length = env->GetArrayLength(tableBytes);
+  buffer = calloc(length, sizeof(jbyte));
+  env->GetByteArrayRegion(tableBytes, 0, length, (jbyte*)buffer);
 
   return hb_blob_create((const char *)buffer, length,
-                         HB_MEMORY_MODE_READONLY,
-                         NULL, _free_nothing);
+                         HB_MEMORY_MODE_WRITABLE,
+                         buffer, free);
 }
 
-
+extern "C" {
 
-hb_face_t*
-hb_jdk_face_create(JDKFontInfo *jdkFontInfo,
-                   hb_destroy_func_t destroy) {
-
-    hb_face_t *face =
-         hb_face_create_for_tables(reference_table, jdkFontInfo, destroy);
-
-    return face;
+/*
+ * Class:     sun_font_SunLayoutEngine
+ * Method:    createFace
+ * Signature: (Lsun/font/Font2D;ZJJ)J
+ */
+JNIEXPORT jlong JNICALL Java_sun_font_SunLayoutEngine_createFace(JNIEnv *env,
+                         jclass cls,
+                         jobject font2D,
+                         jboolean aat,
+                         jlong platformFontPtr) {
+#ifdef MACOSX
+    if (aat && platformFontPtr) {
+        hb_face_t *face = hb_coretext_face_create((CGFontRef)platformFontPtr);
+        return ptr_to_jlong(face);
+    }
+#endif
+    Font2DPtr *fi = (Font2DPtr*)malloc(sizeof(Font2DPtr));
+    if (!fi) {
+        return 0;
+    }
+    JavaVM* vmPtr;
+    env->GetJavaVM(&vmPtr);
+    fi->vmPtr = vmPtr;
+    fi->font2DRef = env->NewWeakGlobalRef(font2D);
+    if (!fi->font2DRef) {
+        free(fi);
+        return 0;
+    }
+    hb_face_t *face = hb_face_create_for_tables(reference_table, fi,
+                                                cleanupFontInfo);
+    return ptr_to_jlong(face);
 }
 
-static hb_font_t* _hb_jdk_font_create(JDKFontInfo *jdkFontInfo,
+/*
+ * Class:     sun_font_SunLayoutEngine
+ * Method:    disposeFace
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_sun_font_SunLayoutEngine_disposeFace(JNIEnv *env,
+                        jclass cls,
+                        jlong ptr) {
+    hb_face_t* face = (hb_face_t*) jlong_to_ptr(ptr);
+    hb_face_destroy(face);
+}
+
+} // extern "C"
+
+static hb_font_t* _hb_jdk_font_create(hb_face_t* face,
+                                      JDKFontInfo *jdkFontInfo,
                                       hb_destroy_func_t destroy) {
 
     hb_font_t *font;
-    hb_face_t *face;
 
-    face = hb_jdk_face_create(jdkFontInfo, destroy);
     font = hb_font_create(face);
-    hb_face_destroy (face);
     hb_font_set_funcs (font,
                        _hb_jdk_get_font_funcs (),
                        jdkFontInfo, (hb_destroy_func_t) _do_nothing);
@@ -387,17 +424,11 @@
 }
 
 #ifdef MACOSX
-static hb_font_t* _hb_jdk_ct_font_create(JDKFontInfo *jdkFontInfo) {
+static hb_font_t* _hb_jdk_ct_font_create(hb_face_t* face,
+                   JDKFontInfo *jdkFontInfo) {
 
     hb_font_t *font = NULL;
-    hb_face_t *face = NULL;
-    if (jdkFontInfo->nativeFont == 0) {
-        return NULL;
-    }
-    face = hb_coretext_face_create((CGFontRef)(jdkFontInfo->nativeFont));
     font = hb_font_create(face);
-    hb_face_destroy(face);
-
     hb_font_set_scale(font,
                      HBFloatToFixed(jdkFontInfo->ptSize),
                      HBFloatToFixed(jdkFontInfo->ptSize));
@@ -405,18 +436,13 @@
 }
 #endif
 
-hb_font_t* hb_jdk_font_create(JDKFontInfo *jdkFontInfo,
+hb_font_t* hb_jdk_font_create(hb_face_t* hbFace,
+                             JDKFontInfo *jdkFontInfo,
                              hb_destroy_func_t destroy) {
-
-   hb_font_t* font = NULL;
-
 #ifdef MACOSX
-     if (jdkFontInfo->aat) {
-         font = _hb_jdk_ct_font_create(jdkFontInfo);
+     if (jdkFontInfo->aat && jdkFontInfo->nativeFont) {
+         return _hb_jdk_ct_font_create(hbFace, jdkFontInfo);
      }
 #endif
-    if (font == NULL) {
-        font = _hb_jdk_font_create(jdkFontInfo, destroy);
-    }
-    return font;
+    return _hb_jdk_font_create(hbFace, jdkFontInfo, destroy);
 }
--- a/src/java.desktop/share/native/libfontmanager/hb-jdk.h	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/native/libfontmanager/hb-jdk.h	Thu Apr 11 10:49:36 2019 -0700
@@ -29,7 +29,6 @@
 #include "hb.h"
 #include <jni.h>
 #include <sunfontids.h>
-#include <fontscalerdefs.h>
 
 # ifdef __cplusplus
 extern "C" {
@@ -40,7 +39,6 @@
     jobject font2D;
     jobject fontStrike;
     long nativeFont;
-    TTLayoutTableCache *layoutTables;
     float matrix[4];
     float ptSize;
     float xPtSize;
@@ -65,7 +63,8 @@
 hb_jdk_face_create(JDKFontInfo*   jdkFontInfo,
                    hb_destroy_func_t destroy);
 hb_font_t *
-hb_jdk_font_create(JDKFontInfo*   jdkFontInfo,
+hb_jdk_font_create(hb_face_t* hbFace,
+                   JDKFontInfo*   jdkFontInfo,
                    hb_destroy_func_t destroy);
 
 
--- a/src/java.desktop/share/native/libfontmanager/sunFont.c	Thu Apr 11 14:20:16 2019 +0530
+++ b/src/java.desktop/share/native/libfontmanager/sunFont.c	Thu Apr 11 10:49:36 2019 -0700
@@ -344,32 +344,3 @@
 
     (*env)->ReleasePrimitiveArrayCritical(env, results, nresults, 0);
 }
-
-JNIEXPORT TTLayoutTableCache* newLayoutTableCache() {
-  TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache));
-  if (ltc) {
-    int i;
-    for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
-      ltc->entries[i].len = -1;
-    }
-    ltc->entries[0].tag = GDEF_TAG;
-    ltc->entries[1].tag = GPOS_TAG;
-    ltc->entries[2].tag = GSUB_TAG;
-    ltc->entries[3].tag = HEAD_TAG;
-    ltc->entries[4].tag = KERN_TAG;
-    ltc->entries[5].tag = MORT_TAG;
-    ltc->entries[6].tag = MORX_TAG;
-  }
-  return ltc;
-}
-
-JNIEXPORT void freeLayoutTableCache(TTLayoutTableCache* ltc) {
-  if (ltc) {
-    int i;
-    for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
-      if(ltc->entries[i].ptr) free (ltc->entries[i].ptr);
-    }
-    if (ltc->kernPairs) free(ltc->kernPairs);
-    free(ltc);
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/awt/font/TextLayout/FontLayoutStressTest.java	Thu Apr 11 10:49:36 2019 -0700
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8220231
+ * @summary Cache HarfBuzz face object for same font's text layout calls
+ * @comment Test layout operations for the same font performed simultaneously
+ *          from multiple threads
+ */
+
+import java.awt.Font;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class FontLayoutStressTest {
+    private static final int NUMBER_OF_THREADS =
+            Runtime.getRuntime().availableProcessors() * 2;
+    private static final long TIME_TO_RUN_NS = 1_000_000_000; // 1 second
+    private static final Font FONT = new Font(Font.SERIF, Font.PLAIN, 12);
+    private static final FontRenderContext FRC = new FontRenderContext(null,
+            false, false);
+    private static final char[] TEXT = "Lorem ipsum dolor sit amet, ..."
+            .toCharArray();
+
+    private static double doLayout() {
+        GlyphVector gv = FONT.layoutGlyphVector(FRC, TEXT, 0, TEXT.length,
+                Font.LAYOUT_LEFT_TO_RIGHT);
+        return gv.getGlyphPosition(gv.getNumGlyphs()).getX();
+    }
+
+    public static void main(String[] args) throws Throwable {
+        double expectedWidth = doLayout();
+        AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+        CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
+        List<Thread> threads = new ArrayList<>();
+        for (int i = 0; i < NUMBER_OF_THREADS; i++) {
+            Thread thread = new Thread(() -> {
+                try {
+                    barrier.await();
+                    long timeToStop = System.nanoTime() + TIME_TO_RUN_NS;
+                    while (System.nanoTime() < timeToStop) {
+                        double width = doLayout();
+                        if (width != expectedWidth) {
+                            throw new RuntimeException(
+                                    "Unexpected layout result");
+                        }
+                    }
+                } catch (Throwable e) {
+                    throwableRef.set(e);
+                }
+            });
+            threads.add(thread);
+            thread.start();
+        }
+        for (Thread thread : threads) {
+            thread.join();
+        }
+        Throwable throwable = throwableRef.get();
+        if (throwable != null) {
+            throw throwable;
+        }
+    }
+}