History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/font/StrikeCache.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/font/StrikeCache.java+444−0
@@ -0,0 +1,444 @@
1/*
2 * Copyright (c) 2003, 2011, 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
26package sun.font;
27
28import java.awt.GraphicsConfiguration;
29import java.awt.GraphicsEnvironment;
30import java.lang.ref.Reference;
31import java.lang.ref.ReferenceQueue;
32import java.lang.ref.SoftReference;
33import java.lang.ref.WeakReference;
34import java.util.*;
35
36import sun.java2d.Disposer;
37import sun.java2d.pipe.BufferedContext;
38import sun.java2d.pipe.RenderQueue;
39import sun.java2d.pipe.hw.AccelGraphicsConfig;
40import sun.misc.Unsafe;
41
42/**
43
44A FontStrike is the keeper of scaled glyph image data which is expensive
45to compute so needs to be cached.
46So long as that data may be being used it cannot be invalidated.
47Yet we also need to limit the amount of native memory and number of
48strike objects in use.
49For scaleability and ease of use, a key goal is multi-threaded read
50access to a strike, so that it may be shared by multiple client objects,
51potentially executing on different threads, with no special reference
52counting or "check-out/check-in" requirements which would pass on the
53burden of keeping track of strike references to the SG2D and other clients.
54
55A cache of strikes is maintained via Reference objects.
56This helps in two ways :
571. The VM will free references when memory is low or they have not been
58used in a long time.
592. Reference queues provide a way to get notification of this so we can
60free native memory resources.
61
62 */
63
64public 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 agressively.
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 (!ge.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}