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