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 }