Skip to content

Commit fe83b7d

Browse files
YaaZAlexey Ushakov
authored and
Alexey Ushakov
committedOct 17, 2024
8339341: SurfaceManager cacheMap retains strong references
Reviewed-by: jdv, prr
1 parent 12551ae commit fe83b7d

18 files changed

+121
-123
lines changed
 

‎src/java.desktop/macosx/classes/sun/awt/CGraphicsConfig.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,27 @@
3131
import java.awt.geom.AffineTransform;
3232
import java.awt.image.ColorModel;
3333

34+
import sun.awt.image.SurfaceManager;
3435
import sun.java2d.SurfaceData;
3536
import sun.lwawt.LWGraphicsConfig;
3637
import sun.lwawt.macosx.CFRetainedResource;
3738

3839
public abstract class CGraphicsConfig extends GraphicsConfiguration
39-
implements LWGraphicsConfig {
40+
implements LWGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig {
4041

4142
private final CGraphicsDevice device;
4243
private ColorModel colorModel;
44+
private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache();
4345

4446
protected CGraphicsConfig(CGraphicsDevice device) {
4547
this.device = device;
4648
}
4749

50+
@Override
51+
public SurfaceManager.ProxyCache getSurfaceDataProxyCache() {
52+
return surfaceDataProxyCache;
53+
}
54+
4855
@Override
4956
public final Rectangle getBounds() {
5057
return device.getBounds();

‎src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java

+1-7
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import sun.awt.CGraphicsDevice;
3030
import sun.awt.image.OffScreenImage;
3131
import sun.awt.image.SunVolatileImage;
32-
import sun.awt.image.SurfaceManager;
3332
import sun.java2d.Disposer;
3433
import sun.java2d.DisposerRecord;
3534
import sun.java2d.Surface;
@@ -70,7 +69,7 @@
7069
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER;
7170

7271
public final class MTLGraphicsConfig extends CGraphicsConfig
73-
implements AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
72+
implements AccelGraphicsConfig
7473
{
7574
private static ImageCapabilities imageCaps = new MTLImageCaps();
7675

@@ -112,11 +111,6 @@ private MTLGraphicsConfig(CGraphicsDevice device,
112111
new MTLGCDisposerRecord(pConfigInfo));
113112
}
114113

115-
@Override
116-
public Object getProxyKey() {
117-
return this;
118-
}
119-
120114
public SurfaceData createManagedSurface(int w, int h, int transparency) {
121115
return MTLSurfaceData.createData(this, w, h,
122116
getColorModel(transparency),

‎src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ private MTLSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
156156
super(getCustomSurfaceType(type), cm);
157157
this.graphicsConfig = gc;
158158
this.type = type;
159-
setBlitProxyKey(gc.getProxyKey());
159+
setBlitProxyCache(gc.getSurfaceDataProxyCache());
160160

161161
// TEXTURE shouldn't be scaled, it is used for managed BufferedImages.
162162
scale = type == TEXTURE ? 1 : gc.getDevice().getScaleFactor();

‎src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java

-5
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ private CGLGraphicsConfig(CGraphicsDevice device, long configInfo,
105105
new CGLGCDisposerRecord(pConfigInfo));
106106
}
107107

108-
@Override
109-
public Object getProxyKey() {
110-
return this;
111-
}
112-
113108
@Override
114109
public SurfaceData createManagedSurface(int w, int h, int transparency) {
115110
return CGLSurfaceData.createData(this, w, h,

‎src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java

+61-57
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@
3131
import java.awt.ImageCapabilities;
3232
import java.awt.image.BufferedImage;
3333
import java.awt.image.VolatileImage;
34+
import java.lang.ref.WeakReference;
35+
import java.util.Collections;
3436
import java.util.Iterator;
35-
import java.util.concurrent.ConcurrentHashMap;
37+
import java.util.Map;
38+
import java.util.WeakHashMap;
3639

3740
import sun.java2d.InvalidPipeException;
3841
import sun.java2d.SurfaceData;
@@ -89,47 +92,14 @@ public static void setManager(Image img, SurfaceManager mgr) {
8992
imgaccessor.setSurfaceManager(img, mgr);
9093
}
9194

92-
private volatile ConcurrentHashMap<Object,Object> cacheMap;
93-
94-
/**
95-
* Return an arbitrary cached object for an arbitrary cache key.
96-
* Other objects can use this mechanism to store cached data about
97-
* the source image that will let them save time when using or
98-
* manipulating the image in the future.
99-
* <p>
100-
* Note that the cache is maintained as a simple Map with no
101-
* attempts to keep it up to date or invalidate it so any data
102-
* stored here must either not be dependent on the state of the
103-
* image or it must be individually tracked to see if it is
104-
* outdated or obsolete.
105-
* <p>
106-
* The SurfaceData object of the primary (destination) surface
107-
* has a StateTracker mechanism which can help track the validity
108-
* and "currentness" of any data stored here.
109-
* For convenience and expediency an object stored as cached
110-
* data may implement the FlushableCacheData interface specified
111-
* below so that it may be notified immediately if the flush()
112-
* method is ever called.
113-
*/
114-
public Object getCacheData(Object key) {
115-
return (cacheMap == null) ? null : cacheMap.get(key);
116-
}
117-
11895
/**
119-
* Store an arbitrary cached object for an arbitrary cache key.
120-
* See the getCacheData() method for notes on tracking the
121-
* validity of data stored using this mechanism.
96+
* This map holds references to SurfaceDataProxy per given ProxyCache.
97+
* Unlike ProxyCache, which contains SurfaceDataProxy objects per given SurfaceManager,
98+
* this map does not prevent contained proxies from being garbage collected.
99+
* Therefore, ProxyCache can be considered an "owning" container for the SurfaceDataProxy objects,
100+
* and this map is just a weak mapping for the bookkeeping purposes.
122101
*/
123-
public void setCacheData(Object key, Object value) {
124-
if (cacheMap == null) {
125-
synchronized (this) {
126-
if (cacheMap == null) {
127-
cacheMap = new ConcurrentHashMap<>(2);
128-
}
129-
}
130-
}
131-
cacheMap.put(key, value);
132-
}
102+
private final Map<ProxyCache, WeakReference<SurfaceDataProxy>> weakCache = new WeakHashMap<>(2);
133103

134104
/**
135105
* Returns the main SurfaceData object that "owns" the pixels for
@@ -202,12 +172,10 @@ public boolean isAccelerated() {
202172
tmpGc = GraphicsEnvironment.getLocalGraphicsEnvironment().
203173
getDefaultScreenDevice().getDefaultConfiguration();
204174
}
205-
if (tmpGc instanceof ProxiedGraphicsConfig) {
206-
Object proxyKey =
207-
((ProxiedGraphicsConfig) tmpGc).getProxyKey();
208-
if (proxyKey != null) {
209-
SurfaceDataProxy sdp =
210-
(SurfaceDataProxy) getCacheData(proxyKey);
175+
if (tmpGc instanceof ProxiedGraphicsConfig pgc) {
176+
ProxyCache cache = pgc.getSurfaceDataProxyCache();
177+
if (cache != null) {
178+
SurfaceDataProxy sdp = cache.get(SurfaceManager.this);
211179
return (sdp != null && sdp.isAccelerated());
212180
}
213181
}
@@ -222,13 +190,51 @@ public boolean isAccelerated() {
222190
* Implementing this interface facilitates the default
223191
* implementation of getImageCapabilities() above.
224192
*/
225-
public static interface ProxiedGraphicsConfig {
193+
public interface ProxiedGraphicsConfig {
194+
226195
/**
227-
* Return the key that destination surfaces created on the
196+
* Return the cache that destination surfaces created on the
228197
* given GraphicsConfiguration use to store SurfaceDataProxy
229198
* objects for their cached copies.
230199
*/
231-
public Object getProxyKey();
200+
ProxyCache getSurfaceDataProxyCache();
201+
}
202+
203+
public static class ProxyCache {
204+
private final Map<SurfaceManager, SurfaceDataProxy> map = Collections.synchronizedMap(new WeakHashMap<>());
205+
206+
/**
207+
* Return a cached SurfaceDataProxy object for a given SurfaceManager.
208+
* <p>
209+
* Note that the cache is maintained as a simple Map with no
210+
* attempts to keep it up to date or invalidate it so any data
211+
* stored here must either not be dependent on the state of the
212+
* image or it must be individually tracked to see if it is
213+
* outdated or obsolete.
214+
* <p>
215+
* The SurfaceData object of the primary (destination) surface
216+
* has a StateTracker mechanism which can help track the validity
217+
* and "currentness" of any data stored here.
218+
* For convenience and expediency an object stored as cached
219+
* data may implement the FlushableCacheData interface specified
220+
* below so that it may be notified immediately if the flush()
221+
* method is ever called.
222+
*/
223+
public SurfaceDataProxy get(SurfaceManager manager) {
224+
return map.get(manager);
225+
}
226+
227+
/**
228+
* Store a cached SurfaceDataProxy object for a given SurfaceManager.
229+
* See the get() method for notes on tracking the
230+
* validity of data stored using this mechanism.
231+
*/
232+
public void put(SurfaceManager manager, SurfaceDataProxy proxy) {
233+
synchronized (manager.weakCache) { // Synchronize on weakCache first!
234+
manager.weakCache.put(this, new WeakReference<>(proxy));
235+
map.put(manager, proxy);
236+
}
237+
}
232238
}
233239

234240
/**
@@ -244,15 +250,13 @@ public synchronized void flush() {
244250
flush(false);
245251
}
246252

247-
synchronized void flush(boolean deaccelerate) {
248-
if (cacheMap != null) {
249-
Iterator<Object> i = cacheMap.values().iterator();
253+
void flush(boolean deaccelerate) {
254+
synchronized (weakCache) {
255+
Iterator<WeakReference<SurfaceDataProxy>> i = weakCache.values().iterator();
250256
while (i.hasNext()) {
251-
Object o = i.next();
252-
if (o instanceof FlushableCacheData) {
253-
if (((FlushableCacheData) o).flush(deaccelerate)) {
254-
i.remove();
255-
}
257+
SurfaceDataProxy sdp = i.next().get();
258+
if (sdp == null || sdp.flush(deaccelerate)) {
259+
i.remove();
256260
}
257261
}
258262
}

‎src/java.desktop/share/classes/sun/java2d/SurfaceData.java

+21-26
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public abstract class SurfaceData
113113

114114
private static native void initIDs();
115115

116-
private Object blitProxyKey;
116+
private SurfaceManager.ProxyCache blitProxyCache;
117117
private StateTrackableDelegate stateDelegate;
118118

119119
static {
@@ -143,23 +143,21 @@ protected SurfaceData(State state) {
143143
}
144144

145145
/**
146-
* Subclasses can set a "blit proxy key" which will be used
147-
* along with the SurfaceManager.getCacheData() mechanism to
146+
* Subclasses can set a "blit proxy cache" which will be used
147+
* along with the SurfaceManager to
148148
* store acceleration-compatible cached copies of source images.
149-
* This key is a "tag" used to identify which cached copies
150-
* are compatible with this destination SurfaceData.
151-
* The getSourceSurfaceData() method uses this key to manage
152-
* cached copies of a source image as described below.
149+
* The getSourceSurfaceData() method uses this cache to manage
150+
* copies of a source image as described below.
153151
* <p>
154-
* The Object used as this key should be as unique as it needs
152+
* The cache used should be as unique as it needs
155153
* to be to ensure that multiple acceleratible destinations can
156-
* each store their cached copies separately under different keys
154+
* each store their cached copies separately into different caches
157155
* without interfering with each other or getting back the wrong
158156
* cached copy.
159157
* <p>
160-
* Many acceleratable SurfaceData objects can use their own
161-
* GraphicsConfiguration as their proxy key as the GC object will
162-
* typically be unique to a given screen and pixel format, but
158+
* Many GraphicsConfiguration implementations have their own
159+
* cache as the GC object is
160+
* typically unique to a given screen and pixel format, but
163161
* other rendering destinations may have more or less stringent
164162
* sharing requirements. For instance, X11 pixmaps can be
165163
* shared on a given screen by any GraphicsConfiguration that
@@ -168,14 +166,14 @@ protected SurfaceData(State state) {
168166
* a different cached proxy for each would be a waste. One can
169167
* imagine platforms where a single cached copy can be created
170168
* and shared across all screens and pixel formats - such
171-
* implementations could use a single heavily shared key Object.
169+
* implementations could use a single heavily shared cache object.
172170
*/
173-
protected void setBlitProxyKey(Object key) {
174-
// Caching is effectively disabled if we never have a proxy key
171+
protected void setBlitProxyCache(SurfaceManager.ProxyCache cache) {
172+
// Caching is effectively disabled if we never have a proxy cache
175173
// since the getSourceSurfaceData() method only does caching
176-
// if the key is not null.
174+
// if the cache is not null.
177175
if (SurfaceDataProxy.isCachingAllowed()) {
178-
this.blitProxyKey = key;
176+
this.blitProxyCache = cache;
179177
}
180178
}
181179

@@ -192,7 +190,7 @@ protected void setBlitProxyKey(Object key) {
192190
* appropriate SurfaceDataProxy instance.
193191
* The parameters describe the type of imaging operation being performed.
194192
* <p>
195-
* If a blitProxyKey was supplied by the subclass then it is
193+
* If a blitProxyCache was supplied by the subclass then it is
196194
* used to potentially override the choice of source SurfaceData.
197195
* The outline of this process is:
198196
* <ol>
@@ -201,8 +199,8 @@ protected void setBlitProxyKey(Object key) {
201199
* <li> destSD gets the SurfaceManager of the source Image
202200
* and first retrieves the default SD from it using
203201
* getPrimarySurfaceData()
204-
* <li> destSD uses its "blit proxy key" (if set) to look for
205-
* some cached data stored in the source SurfaceManager
202+
* <li> destSD uses its "blit proxy cache" (if set) to look for
203+
* some cached data corresponding to the the source SurfaceManager
206204
* <li> If the cached data is null then makeProxyFor() is used
207205
* to create some cached data which is stored back in the
208206
* source SurfaceManager under the same key for future uses.
@@ -219,18 +217,15 @@ public SurfaceData getSourceSurfaceData(Image img,
219217
{
220218
SurfaceManager srcMgr = SurfaceManager.getManager(img);
221219
SurfaceData srcData = srcMgr.getPrimarySurfaceData();
222-
if (img.getAccelerationPriority() > 0.0f &&
223-
blitProxyKey != null)
224-
{
225-
SurfaceDataProxy sdp =
226-
(SurfaceDataProxy) srcMgr.getCacheData(blitProxyKey);
220+
if (img.getAccelerationPriority() > 0.0f && blitProxyCache != null) {
221+
SurfaceDataProxy sdp = blitProxyCache.get(srcMgr);
227222
if (sdp == null || !sdp.isValid()) {
228223
if (srcData.getState() == State.UNTRACKABLE) {
229224
sdp = SurfaceDataProxy.UNCACHED;
230225
} else {
231226
sdp = makeProxyFor(srcData);
232227
}
233-
srcMgr.setCacheData(blitProxyKey, sdp);
228+
blitProxyCache.put(srcMgr, sdp);
234229
}
235230
srcData = sdp.replaceData(srcData, txtype, comp, bgColor);
236231
}

‎src/java.desktop/share/classes/sun/java2d/opengl/OGLSurfaceData.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ protected OGLSurfaceData(OGLGraphicsConfig gc,
232232
super(getCustomSurfaceType(type), cm);
233233
this.graphicsConfig = gc;
234234
this.type = type;
235-
setBlitProxyKey(gc.getProxyKey());
235+
setBlitProxyCache(gc.getSurfaceDataProxyCache());
236236
}
237237

238238
@Override

‎src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ public synchronized SurfaceType getSurfaceType() {
180180
}
181181

182182
@Override
183-
public Object getProxyKey() {
184-
return device.getProxyKeyFor(getSurfaceType());
183+
public SurfaceManager.ProxyCache getSurfaceDataProxyCache() {
184+
return device.getProxyCacheFor(getSurfaceType());
185185
}
186186

187187
/**

0 commit comments

Comments
 (0)
Please sign in to comment.