1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (c) 2011 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  * You acknowledge that this software is not designed or intended for use
34  * in the design, construction, operation or maintenance of any nuclear
35  * facility.
36  *
37  * Sun gratefully acknowledges that this software was originally authored
38  * and developed by Kenneth Bradley Russell and Christopher John Kline.
39  */
40 
41 package jogamp.opengl;
42 
43 import java.util.ArrayList;
44 import java.util.IdentityHashMap;
45 import java.util.Iterator;
46 import java.util.Map;
47 import java.util.Set;
48 
49 import com.jogamp.opengl.GLContext;
50 import com.jogamp.opengl.GLException;
51 
52 
53 /** Provides a deterministic mechanism by which OpenGL contexts can share textures
54     and display lists in the face of multithreading and asynchronous
55     context creation. */
56 
57 public class GLContextShareSet {
58   private static final boolean DEBUG = GLContextImpl.DEBUG;
59 
60   // This class is implemented using a HashMap which maps from all shared contexts
61   // to a share set, containing all shared contexts itself.
62 
63   private static final Map<GLContext, ShareSet> shareMap = new IdentityHashMap<GLContext, ShareSet>();
64 
65   private static class ShareSet {
66     private final Map<GLContext, GLContext> createdShares   = new IdentityHashMap<GLContext, GLContext>();
67     private final Map<GLContext, GLContext> destroyedShares = new IdentityHashMap<GLContext, GLContext>();
68 
addNew(final GLContext slave, final GLContext master)69     public final void addNew(final GLContext slave, final GLContext master) {
70         final GLContext preMaster;
71         if ( slave.isCreated() ) {
72             preMaster = createdShares.put(slave, master);
73         } else {
74             preMaster= destroyedShares.put(slave, master);
75         }
76         if( null != preMaster ) {
77             throw new InternalError("State of ShareSet corrupted: Slave "+toHexString(slave.hashCode())+
78                                     " is not new w/ master "+toHexString(preMaster.hashCode()));
79         }
80     }
addIfNew(final GLContext slave, final GLContext master)81     public final void addIfNew(final GLContext slave, final GLContext master) {
82         final GLContext preMaster = getMaster(master);
83         if( null == preMaster ) {
84             addNew(slave, master);
85         }
86     }
87 
getMaster(final GLContext ctx)88     public final GLContext getMaster(final GLContext ctx) {
89         final GLContext c = createdShares.get(ctx);
90         return null != c ? c : destroyedShares.get(ctx);
91     }
92 
getCreatedShares()93     public Set<GLContext> getCreatedShares() {
94         return createdShares.keySet();
95     }
96 
getDestroyedShares()97     public Set<GLContext> getDestroyedShares() {
98         return destroyedShares.keySet();
99     }
100 
contextCreated(final GLContext ctx)101     public void contextCreated(final GLContext ctx) {
102       final GLContext ctxMaster = destroyedShares.remove(ctx);
103       if( null == ctxMaster ) {
104             throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+
105                                     " should have been in destroyed-set");
106       }
107       final GLContext delMaster = createdShares.put(ctx, ctxMaster);
108       if( null != delMaster ) {
109             throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+
110                                     " shouldn't have been in created-set");
111       }
112     }
113 
contextDestroyed(final GLContext ctx)114     public void contextDestroyed(final GLContext ctx) {
115       final GLContext ctxMaster = createdShares.remove(ctx);
116       if( null == ctxMaster ) {
117             throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+
118                                     " should have been in created-set");
119       }
120       final GLContext delMaster = destroyedShares.put(ctx, ctxMaster);
121       if( null != delMaster ) {
122             throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+
123                                     " shouldn't have been in destroyed-set");
124       }
125     }
126   }
127 
128   /** Indicate that contexts <code>slave</code> and
129       <code>master</code> will share textures and display lists. Both
130       must be non-null. */
registerSharing(final GLContext slave, final GLContext master)131   public static synchronized void registerSharing(final GLContext slave, final GLContext master) {
132       if (slave == null || master == null) {
133           throw new IllegalArgumentException("Both slave and master must be non-null");
134       }
135       ShareSet share = entryFor(slave);
136       if ( null == share ) {
137           share = entryFor(master);
138       }
139       if ( null == share ) {
140           share = new ShareSet();
141       }
142       share.addNew(slave, master);
143       share.addIfNew(master, master); // this master could have a different master shared registered earlier!
144       addEntry(slave, share);
145       addEntry(master, share);
146       if (DEBUG) {
147           System.err.println("GLContextShareSet: registereSharing: 1: " +
148                   toHexString(slave.hashCode()) + ", 2: " + toHexString(master.hashCode()));
149       }
150   }
151 
unregisterSharing(final GLContext lastContext)152   public static synchronized void unregisterSharing(final GLContext lastContext) {
153     if (lastContext == null) {
154       throw new IllegalArgumentException("Last context is null");
155     }
156     final ShareSet share = entryFor(lastContext);
157     if (share == null) {
158       throw new GLException("Last context is unknown: "+lastContext);
159     }
160     Set<GLContext> s = share.getCreatedShares();
161     if(s.size()>0) {
162         throw new GLException("Last context's share set contains "+s.size()+" non destroyed context");
163     }
164     s = share.getDestroyedShares();
165     if(s.size()==0) {
166         throw new GLException("Last context's share set contains no destroyed context");
167     }
168     if (DEBUG) {
169       System.err.println("GLContextShareSet: unregisterSharing: " +
170               toHexString(lastContext.hashCode())+", entries: "+s.size());
171     }
172     for(final Iterator<GLContext> iter = s.iterator() ; iter.hasNext() ; ) {
173         final GLContext ctx = iter.next();
174         if(null == removeEntry(ctx)) {
175             throw new GLException("Removal of shareSet for context failed");
176         }
177     }
178   }
179 
180   /** Returns true if the given GLContext is shared, otherwise false. */
isShared(final GLContext context)181   public static synchronized boolean isShared(final GLContext context) {
182     if (context == null) {
183       throw new IllegalArgumentException("context is null");
184     }
185     final ShareSet share = entryFor(context);
186     return share != null;
187   }
188 
189   /**
190    * Returns the shared master GLContext of the given <code>context</code> if shared, otherwise return <code>null</code>.
191    * <p>
192    * Returns the given <code>context</code>, if it is a shared master.
193    * </p>
194    */
getSharedMaster(final GLContext context)195   public static synchronized GLContext getSharedMaster(final GLContext context) {
196     final ShareSet share = entryFor(context);
197     if (share == null) {
198       return null;
199     }
200     return share.getMaster(context);
201   }
202 
getCreatedSharesImpl(final GLContext context)203   private static synchronized Set<GLContext> getCreatedSharesImpl(final GLContext context) {
204     if (context == null) {
205       throw new IllegalArgumentException("context is null");
206     }
207     final ShareSet share = entryFor(context);
208     if (share != null) {
209         return share.getCreatedShares();
210     }
211     return null;
212   }
getDestroyedSharesImpl(final GLContext context)213   private static synchronized Set<GLContext> getDestroyedSharesImpl(final GLContext context) {
214     if (context == null) {
215       throw new IllegalArgumentException("context is null");
216     }
217     final ShareSet share = entryFor(context);
218     if (share != null) {
219         return share.getDestroyedShares();
220     }
221     return null;
222   }
223 
224   /** Returns true if the given GLContext has shared and created GLContext left including itself, otherwise false. */
hasCreatedSharedLeft(final GLContext context)225   public static synchronized boolean hasCreatedSharedLeft(final GLContext context) {
226       final Set<GLContext> s = getCreatedSharesImpl(context);
227       return null != s && s.size() > 0;
228   }
229 
230   /** Returns a new array-list of created GLContext shared with the given GLContext. */
getCreatedShares(final GLContext context)231   public static synchronized ArrayList<GLContext> getCreatedShares(final GLContext context) {
232       final ArrayList<GLContext> otherShares = new ArrayList<GLContext>();
233       final Set<GLContext> createdShares = getCreatedSharesImpl(context);
234       if( null != createdShares ) {
235           for (final Iterator<GLContext> iter = createdShares.iterator(); iter.hasNext(); ) {
236             final GLContext ctx = iter.next();
237             if (ctx != context) {
238                 otherShares.add(ctx);
239             }
240           }
241       }
242       return otherShares;
243   }
244 
245   /** Returns a new array-list of destroyed GLContext shared with the given GLContext. */
getDestroyedShares(final GLContext context)246   public static synchronized ArrayList<GLContext> getDestroyedShares(final GLContext context) {
247       final ArrayList<GLContext> otherShares = new ArrayList<GLContext>();
248       final Set<GLContext> destroyedShares = getDestroyedSharesImpl(context);
249       if( null != destroyedShares ) {
250           for (final Iterator<GLContext> iter = destroyedShares.iterator(); iter.hasNext(); ) {
251             final GLContext ctx = iter.next();
252             if (ctx != context) {
253                 otherShares.add(ctx);
254             }
255           }
256       }
257       return otherShares;
258   }
259 
260   /** Mark the given GLContext as being created. */
contextCreated(final GLContext context)261   public static synchronized boolean contextCreated(final GLContext context) {
262     final ShareSet share = entryFor(context);
263     if (share != null) {
264       share.contextCreated(context);
265       return true;
266     }
267     return false;
268   }
269 
270   /** Mark the given GLContext as being destroyed. */
contextDestroyed(final GLContext context)271   public static synchronized boolean contextDestroyed(final GLContext context) {
272     final ShareSet share = entryFor(context);
273     if (share != null) {
274       share.contextDestroyed(context);
275       return true;
276     }
277     return false;
278   }
279 
280   //----------------------------------------------------------------------
281   // Internals only below this point
282 
283 
entryFor(final GLContext context)284   private static ShareSet entryFor(final GLContext context) {
285     return shareMap.get(context);
286   }
287 
addEntry(final GLContext context, final ShareSet share)288   private static void addEntry(final GLContext context, final ShareSet share) {
289     if (shareMap.get(context) == null) {
290       shareMap.put(context, share);
291     }
292   }
removeEntry(final GLContext context)293   private static ShareSet removeEntry(final GLContext context) {
294     return shareMap.remove(context);
295   }
296 
toHexString(final long hex)297   private static String toHexString(final long hex) {
298     return "0x" + Long.toHexString(hex);
299   }
300 }
301