1 /*
   2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.font;
  27 
  28 import java.awt.GraphicsConfiguration;
  29 import java.awt.GraphicsEnvironment;
  30 import java.lang.ref.Reference;
  31 import java.lang.ref.ReferenceQueue;
  32 import java.lang.ref.SoftReference;
  33 import java.lang.ref.WeakReference;
  34 import java.util.*;
  35 
  36 import sun.java2d.Disposer;
  37 import sun.java2d.pipe.BufferedContext;
  38 import sun.java2d.pipe.RenderQueue;
  39 import sun.java2d.pipe.hw.AccelGraphicsConfig;
  40 import sun.misc.Unsafe;
  41 
  42 /**
  43 
  44 A FontStrike is the keeper of scaled glyph image data which is expensive
  45 to compute so needs to be cached.
  46 So long as that data may be being used it cannot be invalidated.
  47 Yet we also need to limit the amount of native memory and number of
  48 strike objects in use.
  49 For scaleability and ease of use, a key goal is multi-threaded read
  50 access to a strike, so that it may be shared by multiple client objects,
  51 potentially executing on different threads, with no special reference
  52 counting or "check-out/check-in" requirements which would pass on the
  53 burden of keeping track of strike references to the SG2D and other clients.
  54 
  55 A cache of strikes is maintained via Reference objects.
  56 This helps in two ways :
  57 1. The VM will free references when memory is low or they have not been
  58 used in a long time.
  59 2. Reference queues provide a way to get notification of this so we can
  60 free native memory resources.
  61 
  62  */
  63 
  64 public final class StrikeCache {
  65 
  66     static final Unsafe unsafe = Unsafe.getUnsafe();
  67 
  68     static ReferenceQueue refQueue = Disposer.getQueue();
  69 
  70     static ArrayList<GlyphDisposedListener> disposeListeners = new ArrayList<GlyphDisposedListener>(1);
  71 
  72 
  73     /* Reference objects may have their referents cleared when GC chooses.
  74      * During application client start-up there is typically at least one
  75      * GC which causes the hotspot VM to clear soft (not just weak) references
  76      * Thus not only is there a GC pause, but the work done do rasterise
  77      * glyphs that are fairly certain to be needed again almost immediately
  78      * is thrown away. So for performance reasons a simple optimisation is to
  79      * keep up to 8 strong references to strikes to reduce the chance of
  80      * GC'ing strikes that have been used recently. Note that this may not
  81      * suffice in Solaris UTF-8 locales where a single composite strike may be
  82      * composed of 15 individual strikes, plus the composite strike.
  83      * And this assumes the new architecture doesn't maintain strikes for
  84      * natively accessed bitmaps. It may be worth "tuning" the number of
  85      * strikes kept around for the platform or locale.
  86      * Since no attempt is made to ensure uniqueness or ensure synchronized
  87      * access there is no guarantee that this cache will ensure that unique
  88      * strikes are cached. Every time a strike is looked up it is added
  89      * to the current index in this cache. All this cache has to do to be
  90      * worthwhile is prevent excessive cache flushing of strikes that are
  91      * referenced frequently. The logic that adds references here could be
  92      * tweaked to keep only strikes  that represent untransformed, screen
  93      * sizes as that's the typical performance case.
  94      */
  95     static int MINSTRIKES = 8; // can be overridden by property
  96     static int recentStrikeIndex = 0;
  97     static FontStrike[] recentStrikes;
  98     static boolean cacheRefTypeWeak;
  99 
 100     /*
 101      * Native sizes and offsets for glyph cache
 102      * There are 10 values.
 103      */
 104     static int nativeAddressSize;
 105     static int glyphInfoSize;
 106     static int xAdvanceOffset;
 107     static int yAdvanceOffset;
 108     static int boundsOffset;
 109     static int widthOffset;
 110     static int heightOffset;
 111     static int rowBytesOffset;
 112     static int topLeftXOffset;
 113     static int topLeftYOffset;
 114     static int pixelDataOffset;
 115     static int cacheCellOffset;
 116     static int managedOffset;
 117     static long invisibleGlyphPtr;
 118 
 119     /* Native method used to return information used for unsafe
 120      * access to native data.
 121      * return values as follows:-
 122      * arr[0] = size of an address/pointer.
 123      * arr[1] = size of a GlyphInfo
 124      * arr[2] = offset of advanceX
 125      * arr[3] = offset of advanceY
 126      * arr[4] = offset of width
 127      * arr[5] = offset of height
 128      * arr[6] = offset of rowBytes
 129      * arr[7] = offset of topLeftX
 130      * arr[8] = offset of topLeftY
 131      * arr[9] = offset of pixel data.
 132      * arr[10] = address of a GlyphImageRef representing the invisible glyph
 133      */
 134     static native void getGlyphCacheDescription(long[] infoArray);
 135 
 136     static {
 137 
 138         long[] nativeInfo = new long[13];
 139         getGlyphCacheDescription(nativeInfo);
 140         //Can also get address size from Unsafe class :-
 141         //nativeAddressSize = unsafe.addressSize();
 142         nativeAddressSize = (int)nativeInfo[0];
 143         glyphInfoSize     = (int)nativeInfo[1];
 144         xAdvanceOffset    = (int)nativeInfo[2];
 145         yAdvanceOffset    = (int)nativeInfo[3];
 146         widthOffset       = (int)nativeInfo[4];
 147         heightOffset      = (int)nativeInfo[5];
 148         rowBytesOffset    = (int)nativeInfo[6];
 149         topLeftXOffset    = (int)nativeInfo[7];
 150         topLeftYOffset    = (int)nativeInfo[8];
 151         pixelDataOffset   = (int)nativeInfo[9];
 152         invisibleGlyphPtr = nativeInfo[10];
 153         cacheCellOffset = (int) nativeInfo[11];
 154         managedOffset = (int) nativeInfo[12];
 155 
 156         if (nativeAddressSize < 4) {
 157             throw new InternalError("Unexpected address size for font data: " +
 158                                     nativeAddressSize);
 159         }
 160 
 161         java.security.AccessController.doPrivileged(
 162                                     new java.security.PrivilegedAction() {
 163             public Object run() {
 164 
 165                /* Allow a client to override the reference type used to
 166                 * cache strikes. The default is "soft" which hints to keep
 167                 * the strikes around. This property allows the client to
 168                 * override this to "weak" which hint to the GC to free
 169                 * memory more aggressively.
 170                 */
 171                String refType =
 172                    System.getProperty("sun.java2d.font.reftype", "soft");
 173                cacheRefTypeWeak = refType.equals("weak");
 174 
 175                 String minStrikesStr =
 176                     System.getProperty("sun.java2d.font.minstrikes");
 177                 if (minStrikesStr != null) {
 178                     try {
 179                         MINSTRIKES = Integer.parseInt(minStrikesStr);
 180                         if (MINSTRIKES <= 0) {
 181                             MINSTRIKES = 1;
 182                         }
 183                     } catch (NumberFormatException e) {
 184                     }
 185                 }
 186 
 187                 recentStrikes = new FontStrike[MINSTRIKES];
 188 
 189                 return null;
 190             }
 191         });
 192     }
 193 
 194 
 195     static void refStrike(FontStrike strike) {
 196         int index = recentStrikeIndex;
 197         recentStrikes[index] = strike;
 198         index++;
 199         if (index == MINSTRIKES) {
 200             index = 0;
 201         }
 202         recentStrikeIndex = index;
 203     }
 204 
 205     private static final void doDispose(FontStrikeDisposer disposer) {
 206         if (disposer.intGlyphImages != null) {
 207             freeCachedIntMemory(disposer.intGlyphImages,
 208                     disposer.pScalerContext);
 209         } else if (disposer.longGlyphImages != null) {
 210             freeCachedLongMemory(disposer.longGlyphImages,
 211                     disposer.pScalerContext);
 212         } else if (disposer.segIntGlyphImages != null) {
 213             /* NB Now making multiple JNI calls in this case.
 214              * But assuming that there's a reasonable amount of locality
 215              * rather than sparse references then it should be OK.
 216              */
 217             for (int i=0; i<disposer.segIntGlyphImages.length; i++) {
 218                 if (disposer.segIntGlyphImages[i] != null) {
 219                     freeCachedIntMemory(disposer.segIntGlyphImages[i],
 220                             disposer.pScalerContext);
 221                     /* native will only free the scaler context once */
 222                     disposer.pScalerContext = 0L;
 223                     disposer.segIntGlyphImages[i] = null;
 224                 }
 225             }
 226             /* This may appear inefficient but it should only be invoked
 227              * for a strike that never was asked to rasterise a glyph.
 228              */
 229             if (disposer.pScalerContext != 0L) {
 230                 freeCachedIntMemory(new int[0], disposer.pScalerContext);
 231             }
 232         } else if (disposer.segLongGlyphImages != null) {
 233             for (int i=0; i<disposer.segLongGlyphImages.length; i++) {
 234                 if (disposer.segLongGlyphImages[i] != null) {
 235                     freeCachedLongMemory(disposer.segLongGlyphImages[i],
 236                             disposer.pScalerContext);
 237                     disposer.pScalerContext = 0L;
 238                     disposer.segLongGlyphImages[i] = null;
 239                 }
 240             }
 241             if (disposer.pScalerContext != 0L) {
 242                 freeCachedLongMemory(new long[0], disposer.pScalerContext);
 243             }
 244         } else if (disposer.pScalerContext != 0L) {
 245             /* Rarely a strike may have been created that never cached
 246              * any glyphs. In this case we still want to free the scaler
 247              * context.
 248              */
 249             if (longAddresses()) {
 250                 freeCachedLongMemory(new long[0], disposer.pScalerContext);
 251             } else {
 252                 freeCachedIntMemory(new int[0], disposer.pScalerContext);
 253             }
 254         }
 255     }
 256 
 257     private static boolean longAddresses() {
 258         return nativeAddressSize == 8;
 259     }
 260 
 261     static void disposeStrike(final FontStrikeDisposer disposer) {
 262         // we need to execute the strike disposal on the rendering thread
 263         // because they may be accessed on that thread at the time of the
 264         // disposal (for example, when the accel. cache is invalidated)
 265 
 266         // Whilst this is a bit heavyweight, in most applications
 267         // strike disposal is a relatively infrequent operation, so it
 268         // doesn't matter. But in some tests that use vast numbers
 269         // of strikes, the switching back and forth is measurable.
 270         // So the "pollRemove" call is added to batch up the work.
 271         // If we are polling we know we've already been called back
 272         // and can directly dispose the record.
 273         // Also worrisome is the necessity of getting a GC here.
 274 
 275         if (Disposer.pollingQueue) {
 276             doDispose(disposer);
 277             return;
 278         }
 279 
 280         RenderQueue rq = null;
 281         GraphicsEnvironment ge =
 282             GraphicsEnvironment.getLocalGraphicsEnvironment();
 283         if (!GraphicsEnvironment.isHeadless()) {
 284             GraphicsConfiguration gc =
 285                 ge.getDefaultScreenDevice().getDefaultConfiguration();
 286             if (gc instanceof AccelGraphicsConfig) {
 287                 AccelGraphicsConfig agc = (AccelGraphicsConfig)gc;
 288                 BufferedContext bc = agc.getContext();
 289                 if (bc != null) {
 290                     rq = bc.getRenderQueue();
 291                 }
 292             }
 293         }
 294         if (rq != null) {
 295             rq.lock();
 296             try {
 297                 rq.flushAndInvokeNow(new Runnable() {
 298                     public void run() {
 299                         doDispose(disposer);
 300                         Disposer.pollRemove();
 301                     }
 302                 });
 303             } finally {
 304                 rq.unlock();
 305             }
 306         } else {
 307             doDispose(disposer);
 308         }
 309     }
 310 
 311     static native void freeIntPointer(int ptr);
 312     static native void freeLongPointer(long ptr);
 313     private static native void freeIntMemory(int[] glyphPtrs, long pContext);
 314     private static native void freeLongMemory(long[] glyphPtrs, long pContext);
 315 
 316     private static void freeCachedIntMemory(int[] glyphPtrs, long pContext) {
 317         synchronized(disposeListeners) {
 318             if (disposeListeners.size() > 0) {
 319                 ArrayList<Long> gids = null;
 320 
 321                 for (int i = 0; i < glyphPtrs.length; i++) {
 322                     if (glyphPtrs[i] != 0 && unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
 323 
 324                         if (gids == null) {
 325                             gids = new ArrayList<Long>();
 326                         }
 327                         gids.add((long) glyphPtrs[i]);
 328                     }
 329                 }
 330 
 331                 if (gids != null) {
 332                     // Any reference by the disposers to the native glyph ptrs
 333                     // must be done before this returns.
 334                     notifyDisposeListeners(gids);
 335                 }
 336             }
 337         }
 338 
 339         freeIntMemory(glyphPtrs, pContext);
 340     }
 341 
 342     private static void  freeCachedLongMemory(long[] glyphPtrs, long pContext) {
 343         synchronized(disposeListeners) {
 344         if (disposeListeners.size() > 0)  {
 345                 ArrayList<Long> gids = null;
 346 
 347                 for (int i=0; i < glyphPtrs.length; i++) {
 348                     if (glyphPtrs[i] != 0
 349                             && unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
 350 
 351                         if (gids == null) {
 352                             gids = new ArrayList<Long>();
 353                         }
 354                         gids.add((long) glyphPtrs[i]);
 355                     }
 356                 }
 357 
 358                 if (gids != null) {
 359                     // Any reference by the disposers to the native glyph ptrs
 360                     // must be done before this returns.
 361                     notifyDisposeListeners(gids);
 362                 }
 363         }
 364         }
 365 
 366         freeLongMemory(glyphPtrs, pContext);
 367     }
 368 
 369     public static void addGlyphDisposedListener(GlyphDisposedListener listener) {
 370         synchronized(disposeListeners) {
 371             disposeListeners.add(listener);
 372         }
 373     }
 374 
 375     private static void notifyDisposeListeners(ArrayList<Long> glyphs) {
 376         for (GlyphDisposedListener listener : disposeListeners) {
 377             listener.glyphDisposed(glyphs);
 378         }
 379     }
 380 
 381     public static Reference getStrikeRef(FontStrike strike) {
 382         return getStrikeRef(strike, cacheRefTypeWeak);
 383     }
 384 
 385     public static Reference getStrikeRef(FontStrike strike, boolean weak) {
 386         /* Some strikes may have no disposer as there's nothing
 387          * for them to free, as they allocated no native resource
 388          * eg, if they did not allocate resources because of a problem,
 389          * or they never hold native resources. So they create no disposer.
 390          * But any strike that reaches here that has a null disposer is
 391          * a potential memory leak.
 392          */
 393         if (strike.disposer == null) {
 394             if (weak) {
 395                 return new WeakReference(strike);
 396             } else {
 397                 return new SoftReference(strike);
 398             }
 399         }
 400 
 401         if (weak) {
 402             return new WeakDisposerRef(strike);
 403         } else {
 404             return new SoftDisposerRef(strike);
 405         }
 406     }
 407 
 408     static interface DisposableStrike {
 409         FontStrikeDisposer getDisposer();
 410     }
 411 
 412     static class SoftDisposerRef
 413         extends SoftReference implements DisposableStrike {
 414 
 415         private FontStrikeDisposer disposer;
 416 
 417         public FontStrikeDisposer getDisposer() {
 418             return disposer;
 419         }
 420 
 421         SoftDisposerRef(FontStrike strike) {
 422             super(strike, StrikeCache.refQueue);
 423             disposer = strike.disposer;
 424             Disposer.addReference(this, disposer);
 425         }
 426     }
 427 
 428     static class WeakDisposerRef
 429         extends WeakReference implements DisposableStrike {
 430 
 431         private FontStrikeDisposer disposer;
 432 
 433         public FontStrikeDisposer getDisposer() {
 434             return disposer;
 435         }
 436 
 437         WeakDisposerRef(FontStrike strike) {
 438             super(strike, StrikeCache.refQueue);
 439             disposer = strike.disposer;
 440             Disposer.addReference(this, disposer);
 441         }
 442     }
 443 
 444 }