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+
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 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+}