0001: /*
0002: * $RCSfile: MasterControl.java,v $
0003: *
0004: * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0006: *
0007: * This code is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU General Public License version 2 only, as
0009: * published by the Free Software Foundation. Sun designates this
0010: * particular file as subject to the "Classpath" exception as provided
0011: * by Sun in the LICENSE file that accompanied this code.
0012: *
0013: * This code is distributed in the hope that it will be useful, but WITHOUT
0014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0016: * version 2 for more details (a copy is included in the LICENSE file that
0017: * accompanied this code).
0018: *
0019: * You should have received a copy of the GNU General Public License version
0020: * 2 along with this work; if not, write to the Free Software Foundation,
0021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0022: *
0023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0024: * CA 95054 USA or visit www.sun.com if you need additional information or
0025: * have any questions.
0026: *
0027: * $Revision: 1.39 $
0028: * $Date: 2008/02/28 20:17:26 $
0029: * $State: Exp $
0030: */
0031:
0032: /*
0033: * Portions of this code were derived from work done by the Blackdown
0034: * group (www.blackdown.org), who did the initial Linux implementation
0035: * of the Java 3D API.
0036: */
0037:
0038: package javax.media.j3d;
0039:
0040: import java.util.*;
0041: import java.awt.*;
0042: import java.util.logging.Level;
0043: import java.util.logging.Logger;
0044:
0045: class MasterControl {
0046:
0047: /**
0048: * Options for the runMonitor
0049: */
0050: static final int CHECK_FOR_WORK = 0;
0051: static final int SET_WORK = 1;
0052: static final int RUN_THREADS = 2;
0053: static final int THREAD_DONE = 3;
0054: static final int SET_WORK_FOR_REQUEST_RENDERER = 5;
0055: static final int RUN_RENDERER_CLEANUP = 6;
0056:
0057: // The thread states for MC
0058: static final int SLEEPING = 0;
0059: static final int RUNNING = 1;
0060: static final int WAITING_FOR_THREADS = 3;
0061: static final int WAITING_FOR_CPU = 4;
0062: static final int WAITING_FOR_RENDERER_CLEANUP = 5;
0063:
0064: // Constants used in renderer thread argument
0065: static final Integer REQUESTRENDER = new Integer(
0066: Renderer.REQUESTRENDER);
0067: static final Integer RENDER = new Integer(Renderer.RENDER);
0068: static final Integer SWAP = new Integer(Renderer.SWAP);
0069:
0070: // Constants used for request from user threads
0071: static final Integer ACTIVATE_VIEW = new Integer(1);
0072: static final Integer DEACTIVATE_VIEW = new Integer(2);
0073: static final Integer START_VIEW = new Integer(3);
0074: static final Integer STOP_VIEW = new Integer(4);
0075: static final Integer REEVALUATE_CANVAS = new Integer(5);
0076: static final Integer UNREGISTER_VIEW = new Integer(6);
0077: static final Integer PHYSICAL_ENV_CHANGE = new Integer(7);
0078: static final Integer INPUTDEVICE_CHANGE = new Integer(8);
0079: static final Integer EMPTY_UNIVERSE = new Integer(9);
0080: static final Integer START_RENDERER = new Integer(10);
0081: static final Integer STOP_RENDERER = new Integer(11);
0082: static final Integer RENDER_ONCE = new Integer(12);
0083: static final Integer FREE_CONTEXT = new Integer(13);
0084: static final Integer FREE_DRAWING_SURFACE = new Integer(14);
0085: static final Integer FREE_MESSAGE = new Integer(15);
0086: static final Integer RESET_CANVAS = new Integer(16);
0087: static final Integer GETBESTCONFIG = new Integer(17);
0088: static final Integer ISCONFIGSUPPORT = new Integer(18);
0089: static final Integer SET_GRAPHICSCONFIG_FEATURES = new Integer(19);
0090: static final Integer SET_QUERYPROPERTIES = new Integer(20);
0091: static final Integer SET_VIEW = new Integer(21);
0092:
0093: // Developer logger for reporting informational messages; see getDevLogger()
0094: private static boolean devLoggerEnabled = false;
0095: private static Logger devLogger;
0096:
0097: // Stats logger for reporting runtime statistics; see getStatsLogger()
0098: private static boolean statsLoggerEnabled = false;
0099: private static Logger statsLogger;
0100:
0101: // Core logger for reporting internal errors, warning, and
0102: // informational messages; see getCoreLogger()
0103: private static boolean coreLoggerEnabled = false;
0104: private static Logger coreLogger;
0105:
0106: // Flag indicating that the rendering pipeline libraries are loaded
0107: private static boolean librariesLoaded = false;
0108:
0109: // Issue 257: flag indicating that we are running in "appletLauncher" mode
0110: // and should use JNLPAppletLauncher to load any native libraries
0111: private static boolean appletLauncher = false;
0112:
0113: /**
0114: * reference to MasterControl thread
0115: */
0116: private MasterControlThread mcThread = null;
0117:
0118: /**
0119: * The list of views that are currently registered
0120: */
0121: private UnorderList views = new UnorderList(1, View.class);
0122:
0123: /**
0124: * by MIK OF CLASSX
0125: *
0126: * the flag to indicate whether the background of the offscreen
0127: * canvas must be transparent or not false by default
0128: */
0129: boolean transparentOffScreen = false;
0130:
0131: /**
0132: * Flag to indicate whether Pbuffers are used for off-screen
0133: * rendering; true by default. Set by the "j3d.usePbuffer"
0134: * property, When this flag is set to false, Bitmap (Windows) or
0135: * Pixmap (UNIX) rendering will be used
0136: */
0137: boolean usePbuffer = true;
0138:
0139: /**
0140: * Flag to indicate whether should renderer view frustum culling is done;
0141: * true by default.
0142: * Set by the -Dj3d.viewFrustumCulling property, When this flag is
0143: * set to false, the renderer view frustum culling is turned off.
0144: */
0145: boolean viewFrustumCulling = true;
0146:
0147: /**
0148: * the flag to indicate whether the geometry should be locked or not
0149: */
0150:
0151: private boolean lockGeometry = false;
0152:
0153: /**
0154: * The number of registered views that are active
0155: */
0156: private int numActiveViews = 0;
0157:
0158: /**
0159: * The list of active universes get from View
0160: */
0161: private UnorderList activeUniverseList = new UnorderList(
0162: VirtualUniverse.class);
0163:
0164: /**
0165: * The list of universes register from View
0166: */
0167: private UnorderList regUniverseList = new UnorderList(
0168: VirtualUniverse.class);
0169:
0170: /**
0171: * A lock used for accessing time structures.
0172: */
0173: private Object timeLock = new Object();
0174:
0175: /**
0176: * The current "time" value
0177: */
0178: private long time = 0;
0179:
0180: /**
0181: * Use to assign threadOpts in Renderer thread.
0182: */
0183: private long waitTimestamp = 0;
0184:
0185: /**
0186: * The current list of work threads
0187: */
0188: private UnorderList stateWorkThreads = new UnorderList(
0189: J3dThreadData.class);
0190: private UnorderList renderWorkThreads = new UnorderList(
0191: J3dThreadData.class);
0192: private UnorderList requestRenderWorkThreads = new UnorderList(
0193: J3dThreadData.class);
0194:
0195: /**
0196: * The current list of work threads
0197: */
0198: private UnorderList renderThreadData = new UnorderList(
0199: J3dThreadData.class);
0200:
0201: /**
0202: * The list of input device scheduler thread
0203: */
0204: private UnorderList inputDeviceThreads = new UnorderList(1,
0205: InputDeviceScheduler.class);
0206:
0207: /**
0208: * A flag that is true when the thread lists need updating
0209: */
0210: private boolean threadListsChanged;
0211:
0212: /**
0213: * Markers for the last transform structure update thread
0214: * and the last update thread.
0215: */
0216: private int lastTransformStructureThread = 0;
0217: private int lastStructureUpdateThread = 0;
0218:
0219: /**
0220: * The current time snapshots
0221: */
0222: private long currentTime;
0223:
0224: // Only one Timer thread in the system.
0225: TimerThread timerThread;
0226:
0227: // Only one Notification thread in the system.
0228: private NotificationThread notificationThread;
0229:
0230: /**
0231: * This flag indicates that MC is running
0232: */
0233: volatile boolean running = true;
0234:
0235: /**
0236: * This flag indicates that MC has work to do
0237: */
0238: private boolean workToDo = false;
0239:
0240: /**
0241: * This flag indicates that there is work for requestRenderer
0242: */
0243: private boolean requestRenderWorkToDo = false;
0244:
0245: /**
0246: * The number of THREAD_DONE messages pending
0247: */
0248: private int threadPending = 0;
0249: private int renderPending = 0;
0250: private int statePending = 0;
0251:
0252: /**
0253: * State variables for work lists
0254: */
0255: private boolean renderWaiting = false;
0256: private boolean stateWaiting = false;
0257:
0258: /**
0259: * The current state of the MC thread
0260: */
0261: private int state = SLEEPING;
0262:
0263: // time for sleep in order to met the minimum frame duration
0264: private long sleepTime = 0;
0265:
0266: /**
0267: * The number of cpu's Java 3D may use
0268: */
0269: private int cpuLimit;
0270:
0271: /**
0272: * A list of mirror objects to be updated
0273: */
0274: private UnorderList mirrorObjects = new UnorderList(
0275: ObjectUpdate.class);
0276:
0277: /**
0278: * The renderingAttributesStructure for updating node component
0279: * objects
0280: */
0281: private RenderingAttributesStructure renderingAttributesStructure = new RenderingAttributesStructure();
0282:
0283: /**
0284: * The default rendering method
0285: */
0286: private DefaultRenderMethod defaultRenderMethod = null;
0287:
0288: /**
0289: * The text3D rendering method
0290: */
0291: private Text3DRenderMethod text3DRenderMethod = null;
0292:
0293: /**
0294: * The vertex array rendering method
0295: */
0296: private VertexArrayRenderMethod vertexArrayRenderMethod = null;
0297:
0298: /**
0299: * The displayList rendering method
0300: */
0301: private DisplayListRenderMethod displayListRenderMethod = null;
0302:
0303: /**
0304: * The compressed geometry rendering method
0305: */
0306: private CompressedGeometryRenderMethod compressedGeometryRenderMethod = null;
0307:
0308: /**
0309: * The oriented shape3D rendering method
0310: */
0311: private OrientedShape3DRenderMethod orientedShape3DRenderMethod = null;
0312:
0313: /**
0314: * This is the start time upon which alpha's and behaviors
0315: * are synchronized to. It is initialized once, the first time
0316: * that a MasterControl object is created.
0317: */
0318: static long systemStartTime = 0L;
0319:
0320: // Flag indicating that we are on a Windows OS
0321: private static boolean isWindowsOs = false;
0322:
0323: // Flag indicating we are on MacOS
0324: private static boolean isMacOs = false;
0325:
0326: // This is a counter for texture id's, valid id starts from 1
0327: private int textureIdCount = 0;
0328:
0329: // This is lock for both 2D/3D textureIds;
0330: private Object textureIdLock = new Object();
0331:
0332: // This is a time stamp used when context is created
0333: private long contextTimeStamp = 0;
0334:
0335: // This is an array of canvasIds in used
0336: private boolean[] canvasIds = null;
0337: private int canvasFreeIndex = 0;
0338: private Object canvasIdLock = new Object();
0339:
0340: // This is a counter for rendererBit
0341: private int rendererCount = 0;
0342:
0343: // Flag that indicates whether to shared display context or not
0344: boolean isSharedCtx = false;
0345:
0346: // Flag that tells us to use NV_register_combiners
0347: boolean useCombiners = false;
0348:
0349: // Flag that indicates whether compile is disabled or not
0350: boolean disableCompile = false;
0351:
0352: // Flag that indicates whether or not compaction occurs
0353: boolean doCompaction = true;
0354:
0355: // Flag that indicates whether separate specular color is disabled or not
0356: boolean disableSeparateSpecularColor = false;
0357:
0358: // Flag that indicates whether DisplayList is used or not
0359: boolean isDisplayList = true;
0360:
0361: // If this flag is set, then by-ref geometry will not be
0362: // put in display list
0363: boolean buildDisplayListIfPossible = false;
0364:
0365: // If this flag is set, then geometry arrays with vertex attributes can
0366: // be in display list.
0367: boolean vertexAttrsInDisplayList = false;
0368:
0369: // Issue 249 - flag that indicates whether the soleUser optimization is permitted
0370: boolean allowSoleUser = false;
0371:
0372: // Issue 266 - Flag indicating whether null graphics configs are allowed
0373: // Set by -Dj3d.allowNullGraphicsConfig property
0374: // Setting this flag causes Canvas3D to allow a null GraphicsConfiguration
0375: // for on-screen canvases. This is only for backward compatibility with
0376: // legacy applications.
0377: boolean allowNullGraphicsConfig = false;
0378:
0379: // Issue 239 - Flag indicating whether the stencil buffer is cleared by
0380: // default each frame when the color and depth buffers are cleared.
0381: // Note that this is a partial solution, since we eventually want an API
0382: // to control this.
0383: boolean stencilClear = false;
0384:
0385: // The global shading language being used. Using a ShaderProgram
0386: // with a shading language other than the one specified by
0387: // globalShadingLanguage will cause a ShaderError to be generated,
0388: static int globalShadingLanguage = Shader.SHADING_LANGUAGE_GLSL;
0389:
0390: // Flags indicating whether the Cg or GLSL libraries are available; we still need
0391: // to check for the actual extension support when the Canvas3D with its associated context
0392: // is created. Note that these are qualifed by the above globalShadingLanguage, so at
0393: // most one of these two flags will be true;
0394: static boolean cgLibraryAvailable = false;
0395: static boolean glslLibraryAvailable = false;
0396:
0397: // REQUESTCLEANUP messages argument
0398: static Integer REMOVEALLCTXS_CLEANUP = new Integer(1);
0399: static Integer REMOVECTX_CLEANUP = new Integer(2);
0400: static Integer REMOVENOTIFY_CLEANUP = new Integer(3);
0401: static Integer RESETCANVAS_CLEANUP = new Integer(4);
0402: static Integer FREECONTEXT_CLEANUP = new Integer(5);
0403:
0404: // arguments for renderer resource cleanup run
0405: Object rendererCleanupArgs[] = {
0406: new Integer(Renderer.REQUESTCLEANUP), null, null };
0407:
0408: // Context creation should obtain this lock, so that
0409: // first_time and all the extension initilialization
0410: // are done in the MT safe manner
0411: Object contextCreationLock = new Object();
0412:
0413: // Flag that indicates whether to lock the DSI while rendering
0414: boolean doDsiRenderLock = false;
0415:
0416: // Flag that indicates the pre-1.5 behavior of enforcing power-of-two
0417: // textures. If set, then any non-power-of-two textures will throw an
0418: // exception.
0419: boolean enforcePowerOfTwo = false;
0420:
0421: // Flag that indicates whether the framebuffer is sharing the
0422: // Z-buffer with both the left and right eyes when in stereo mode.
0423: // If this is true, we need to clear the Z-buffer between rendering
0424: // to the left and right eyes.
0425: boolean sharedStereoZBuffer = true;
0426:
0427: // True to disable all underlying multisampling API so it uses
0428: // the setting in the driver.
0429: boolean implicitAntialiasing = false;
0430:
0431: // False to disable compiled vertex array extensions if support
0432: boolean isCompiledVertexArray = true;
0433:
0434: // Number of reserved vertex attribute locations for GLSL (must be at
0435: // least 1).
0436: // Issue 269 - need to reserve up to 6 vertex attribtue locations to ensure
0437: // that we don't collide with a predefined gl_* attribute on nVidia cards.
0438: int glslVertexAttrOffset = 6;
0439:
0440: // Hashtable that maps a GraphicsDevice to its associated
0441: // Screen3D--this is only used for on-screen Canvas3Ds
0442: Hashtable deviceScreenMap = new Hashtable();
0443:
0444: // Use to store all requests from user threads.
0445: UnorderList requestObjList = new UnorderList();
0446: private UnorderList requestTypeList = new UnorderList(Integer.class);
0447:
0448: // Temporary storage to store stop request for requestViewList
0449: private UnorderList tempViewList = new UnorderList();
0450: private UnorderList renderOnceList = new UnorderList();
0451:
0452: // This flag is true when there is pending request
0453: // i.e. false when the above requestxxx Lists are all empty.
0454: private boolean pendingRequest = false;
0455:
0456: // Root ThreadGroup for creating Java 3D threads
0457: private static ThreadGroup rootThreadGroup;
0458:
0459: // Thread priority for all Java 3D threads
0460: private static int threadPriority;
0461:
0462: static private Object mcThreadLock = new Object();
0463:
0464: private ArrayList timestampUpdateList = new ArrayList(3);
0465:
0466: private UnorderList freeMessageList = new UnorderList(8);
0467:
0468: // Native AWT object
0469: long awt;
0470:
0471: // Maximum number of lights
0472: int maxLights;
0473:
0474: // This is used for D3D only
0475: int resendTexTimestamp = 0;
0476:
0477: // Indicates that the property to disable Xinerama mode was set and
0478: // successfully disabled.
0479: boolean xineramaDisabled = false;
0480:
0481: // Set by the -Dj3d.sortShape3DBounds property, When this flag is
0482: // set to true, the bounds of the Shape3D node will be used in
0483: // place of the computed GeometryArray bounds for transparency
0484: // sorting for those Shape3D nodes whose boundsAutoCompute
0485: // attribute is set to false.
0486: boolean sortShape3DBounds = false;
0487:
0488: //Set by -Dj3d.forceReleaseView property.
0489: //Setting this flag as true disables the bug fix 4267395 in View deactivate().
0490: //The bug 4267395 can lock-up *some* systems, but the bug fix can
0491: //produce memory leaks in applications which creates and destroy Canvas3D
0492: //from time to time.
0493: //Set as true if you have memory leaks after disposing Canvas3D.
0494: //Default false value does affect Java3D View dispose behavior.
0495: boolean forceReleaseView = false;
0496:
0497: // Issue 561: Set by -Dj3d.releaseBoundingBoxMemory property.
0498: // When set to true, the per-instance fields used in bounding box
0499: // transformation are released at the end of transform methods. This saves
0500: // a significant amount of memory in large scenes containing huge amounts
0501: // of bounding boxes. Setting this false can improve performance when
0502: // lots of transforms are performed. The default is false.
0503: boolean releaseBoundingBoxMemory = false;
0504:
0505: // Issue 480: Cache the bounds of nodes so that getBounds does not
0506: // recompute the boounds of the entire graph per call
0507: boolean cacheAutoComputedBounds = false;
0508:
0509: // issue 544
0510: boolean useBoxForGroupBounds = false;
0511:
0512: /**
0513: * Constructs a new MasterControl object. Note that there is
0514: * exatly one MasterControl object, created statically by
0515: * VirtualUniverse.
0516: */
0517: MasterControl() {
0518: assert librariesLoaded;
0519:
0520: // Get AWT handle
0521: awt = Pipeline.getPipeline().getAWT();
0522:
0523: // Initialize the start time upon which alpha's and behaviors
0524: // are synchronized to (if it isn't already set).
0525: if (systemStartTime == 0L) {
0526: systemStartTime = J3dClock.currentTimeMillis();
0527: }
0528:
0529: if (J3dDebug.devPhase) {
0530: // Check to see whether debug mode is allowed
0531: J3dDebug.debug = getBooleanProperty("j3d.debug", false,
0532: "J3dDebug.debug");
0533: }
0534:
0535: // Check to see whether shared contexts are allowed
0536: if (!isD3D()) {
0537: isSharedCtx = getBooleanProperty("j3d.sharedctx",
0538: isSharedCtx, "shared contexts");
0539: }
0540:
0541: doCompaction = getBooleanProperty("j3d.docompaction",
0542: doCompaction, "compaction");
0543:
0544: // by MIK OF CLASSX
0545: transparentOffScreen = getBooleanProperty(
0546: "j3d.transparentOffScreen", transparentOffScreen,
0547: "transparent OffScreen");
0548:
0549: usePbuffer = getBooleanProperty("j3d.usePbuffer", usePbuffer,
0550: "Off-screen Pbuffer");
0551:
0552: viewFrustumCulling = getBooleanProperty(
0553: "j3d.viewFrustumCulling", viewFrustumCulling,
0554: "View frustum culling in the renderer is");
0555:
0556: sortShape3DBounds = getBooleanProperty("j3d.sortShape3DBounds",
0557: sortShape3DBounds,
0558: "Shape3D bounds enabled for transparency sorting",
0559: "Shape3D bounds *ignored* for transparency sorting");
0560:
0561: forceReleaseView = getBooleanProperty("j3d.forceReleaseView",
0562: forceReleaseView,
0563: "forceReleaseView after Canvas3D dispose enabled",
0564: "forceReleaseView after Canvas3D dispose disabled");
0565:
0566: releaseBoundingBoxMemory = getBooleanProperty(
0567: "j3d.releaseBoundingBoxMemory",
0568: releaseBoundingBoxMemory,
0569: "releasing memory after bounding box transform");
0570:
0571: useCombiners = getBooleanProperty("j3d.usecombiners",
0572: useCombiners,
0573: "Using NV_register_combiners if available",
0574: "NV_register_combiners disabled");
0575:
0576: if (getProperty("j3d.disablecompile") != null) {
0577: disableCompile = true;
0578: System.err.println("Java 3D: BranchGroup.compile disabled");
0579: }
0580:
0581: if (getProperty("j3d.disableSeparateSpecular") != null) {
0582: disableSeparateSpecularColor = true;
0583: System.err
0584: .println("Java 3D: separate specular color disabled if possible");
0585: }
0586:
0587: isDisplayList = getBooleanProperty("j3d.displaylist",
0588: isDisplayList, "display list");
0589:
0590: implicitAntialiasing = getBooleanProperty(
0591: "j3d.implicitAntialiasing", implicitAntialiasing,
0592: "implicit antialiasing");
0593:
0594: isCompiledVertexArray = getBooleanProperty(
0595: "j3d.compiledVertexArray", isCompiledVertexArray,
0596: "compiled vertex array");
0597:
0598: boolean j3dOptimizeSpace = getBooleanProperty(
0599: "j3d.optimizeForSpace", true, "optimize for space");
0600:
0601: if (isDisplayList) {
0602: // Build Display list for by-ref geometry
0603: // ONLY IF optimizeForSpace is false
0604: if (!j3dOptimizeSpace) {
0605: buildDisplayListIfPossible = true;
0606: }
0607:
0608: // Build display lists for geometry with vertex attributes
0609: // ONLY if we are in GLSL mode and GLSL shaders are available
0610: if (glslLibraryAvailable) {
0611: vertexAttrsInDisplayList = true;
0612: }
0613: }
0614:
0615: // Check to see whether Renderer can run without DSI lock
0616: doDsiRenderLock = getBooleanProperty("j3d.renderLock",
0617: doDsiRenderLock, "render lock");
0618:
0619: // Check to see whether we enforce power-of-two textures
0620: enforcePowerOfTwo = getBooleanProperty(
0621: "j3d.textureEnforcePowerOfTwo", enforcePowerOfTwo,
0622: "checking power-of-two textures");
0623:
0624: // Issue 249 - check to see whether the soleUser optimization is permitted
0625: allowSoleUser = getBooleanProperty("j3d.allowSoleUser",
0626: allowSoleUser, "sole-user mode");
0627:
0628: // Issue 266 - check to see whether null graphics configs are allowed
0629: allowNullGraphicsConfig = getBooleanProperty(
0630: "j3d.allowNullGraphicsConfig", allowNullGraphicsConfig,
0631: "null graphics configs");
0632:
0633: // Issue 239 - check to see whether per-frame stencil clear is enabled
0634: stencilClear = getBooleanProperty("j3d.stencilClear",
0635: stencilClear, "per-frame stencil clear");
0636:
0637: // Check to see if stereo mode is sharing the Z-buffer for both eyes.
0638: sharedStereoZBuffer = getBooleanProperty(
0639: "j3d.sharedstereozbuffer", sharedStereoZBuffer,
0640: "shared stereo Z buffer");
0641:
0642: // Get the maximum number of concurrent threads (CPUs)
0643: final int defaultThreadLimit = getNumberOfProcessors() + 1;
0644: Integer threadLimit = (Integer) java.security.AccessController
0645: .doPrivileged(new java.security.PrivilegedAction() {
0646: public Object run() {
0647: return Integer.getInteger("j3d.threadLimit",
0648: defaultThreadLimit);
0649: }
0650: });
0651:
0652: cpuLimit = threadLimit.intValue();
0653: if (cpuLimit < 1)
0654: cpuLimit = 1;
0655: if (J3dDebug.debug || cpuLimit != defaultThreadLimit) {
0656: System.err.println("Java 3D: concurrent threadLimit = "
0657: + cpuLimit);
0658: }
0659:
0660: // Get the input device scheduler sampling time
0661: Integer samplingTime = (Integer) java.security.AccessController
0662: .doPrivileged(new java.security.PrivilegedAction() {
0663: public Object run() {
0664: return Integer.getInteger(
0665: "j3d.deviceSampleTime", 0);
0666: }
0667: });
0668:
0669: if (samplingTime.intValue() > 0) {
0670: InputDeviceScheduler.samplingTime = samplingTime.intValue();
0671: System.err.println("Java 3D: Input device sampling time = "
0672: + samplingTime + " ms");
0673: }
0674:
0675: // Get the glslVertexAttrOffset
0676: final int defaultGLSLVertexAttrOffset = glslVertexAttrOffset;
0677: Integer vattrOffset = (Integer) java.security.AccessController
0678: .doPrivileged(new java.security.PrivilegedAction() {
0679: public Object run() {
0680: return Integer.getInteger(
0681: "j3d.glslVertexAttrOffset",
0682: defaultGLSLVertexAttrOffset);
0683: }
0684: });
0685:
0686: glslVertexAttrOffset = vattrOffset.intValue();
0687: if (glslVertexAttrOffset < 1) {
0688: glslVertexAttrOffset = 1;
0689: }
0690: if (J3dDebug.debug
0691: || glslVertexAttrOffset != defaultGLSLVertexAttrOffset) {
0692: System.err.println("Java 3D: glslVertexAttrOffset = "
0693: + glslVertexAttrOffset);
0694: }
0695:
0696: // See if Xinerama should be disabled for better performance.
0697: boolean disableXinerama = false;
0698: if (getProperty("j3d.disableXinerama") != null) {
0699: disableXinerama = true;
0700: }
0701:
0702: // Issue 480 : Cache bounds returned by getBounds()
0703: cacheAutoComputedBounds = getBooleanProperty(
0704: "j3d.cacheAutoComputeBounds", cacheAutoComputedBounds,
0705: "Cache AutoCompute Bounds, accelerates getBounds()");
0706:
0707: // Issue 544
0708: useBoxForGroupBounds = getBooleanProperty(
0709: "j3d.useBoxForGroupBounds", useBoxForGroupBounds,
0710: "Use of BoundingBox for group geometric bounds");
0711:
0712: // Initialize the native J3D library
0713: if (!Pipeline.getPipeline().initializeJ3D(disableXinerama)) {
0714: throw new RuntimeException(J3dI18N
0715: .getString("MasterControl0"));
0716: }
0717:
0718: if (xineramaDisabled) {
0719: // initializeJ3D() successfully disabled Xinerama.
0720: System.err.println("Java 3D: Xinerama disabled");
0721: } else if (disableXinerama) {
0722: // j3d.disableXinerama is true, but attempt failed.
0723: System.err.println("Java 3D: could not disable Xinerama");
0724: }
0725:
0726: // Check for obsolete properties
0727: String[] obsoleteProps = { "j3d.backgroundtexture",
0728: "j3d.forceNormalized", "j3d.g2ddrawpixel",
0729: "j3d.simulatedMultiTexture", "j3d.useFreeLists", };
0730: for (int i = 0; i < obsoleteProps.length; i++) {
0731: if (getProperty(obsoleteProps[i]) != null) {
0732: System.err.println("Java 3D: " + obsoleteProps[i]
0733: + " property ignored");
0734: }
0735: }
0736:
0737: // Get the maximum Lights
0738: maxLights = Pipeline.getPipeline().getMaximumLights();
0739:
0740: // create the freelists
0741: FreeListManager.createFreeLists();
0742:
0743: // create an array canvas use registers
0744: // The 32 limit can be lifted once the
0745: // resourceXXXMasks in other classes
0746: // are change not to use integer.
0747: canvasIds = new boolean[32];
0748: for (int i = 0; i < canvasIds.length; i++) {
0749: canvasIds[i] = false;
0750: }
0751: canvasFreeIndex = 0;
0752: }
0753:
0754: private static boolean initLogger(Logger logger, Level defaultLevel) {
0755: if (logger == null) {
0756: return false;
0757: }
0758:
0759: if (defaultLevel != null && logger.getLevel() == null
0760: && Logger.getLogger("j3d").getLevel() == null) {
0761:
0762: try {
0763: // Set default logger level rather than inheriting from system global
0764: logger.setLevel(defaultLevel);
0765: } catch (SecurityException ex) {
0766: System.err.println(ex);
0767: return false;
0768: }
0769: }
0770:
0771: return logger.isLoggable(Level.SEVERE);
0772: }
0773:
0774: // Called by the static initializer to initialize the loggers
0775: private static void initLoggers() {
0776: coreLogger = Logger.getLogger("j3d.core");
0777: devLogger = Logger.getLogger("j3d.developer");
0778: statsLogger = Logger.getLogger("j3d.stats");
0779:
0780: java.security.AccessController
0781: .doPrivileged(new java.security.PrivilegedAction() {
0782: public Object run() {
0783: coreLoggerEnabled = initLogger(coreLogger, null);
0784: devLoggerEnabled = initLogger(devLogger,
0785: Level.OFF);
0786: statsLoggerEnabled = initLogger(statsLogger,
0787: Level.OFF);
0788: return null;
0789: }
0790: });
0791: }
0792:
0793: /**
0794: * Get the developer logger -- OFF by default
0795: *
0796: * WARNING - for probable incorrect or inconsistent api usage
0797: * INFO - for informational messages such as performance hints (less verbose than FINE)
0798: * FINE - for informational messages from inner loops
0799: * FINER - using default values which may not be optimal
0800: */
0801: static Logger getDevLogger() {
0802: return devLogger;
0803: }
0804:
0805: static boolean isDevLoggable(Level level) {
0806: return devLoggerEnabled && devLogger.isLoggable(level);
0807: }
0808:
0809: /**
0810: * Get the stats logger -- OFF by default
0811: *
0812: * WARNING - statistical anomalies
0813: * INFO - basic performance stats - not too verbose and minimally intrusive
0814: * FINE - somewhat verbose and intrusive
0815: * FINER - more verbose and intrusive
0816: * FINEST - most verbose and intrusive
0817: */
0818: static Logger getStatsLogger() {
0819: return statsLogger;
0820: }
0821:
0822: static boolean isStatsLoggable(Level level) {
0823: return statsLoggerEnabled && statsLogger.isLoggable(level);
0824: }
0825:
0826: /**
0827: * Get the core logger -- level is INFO by default
0828: *
0829: * SEVERE - Serious internal errors
0830: * WARNING - Possible internal errors or anomalies
0831: * INFO - General informational messages
0832: * FINE - Internal debugging information - somewhat verbose
0833: * FINER - Internal debugging information - more verbose
0834: * FINEST - Internal debugging information - most verbose
0835: */
0836: static Logger getCoreLogger() {
0837: return coreLogger;
0838: }
0839:
0840: static boolean isCoreLoggable(Level level) {
0841: return coreLoggerEnabled && coreLogger.isLoggable(level);
0842: }
0843:
0844: private static String getProperty(final String prop) {
0845: return (String) java.security.AccessController
0846: .doPrivileged(new java.security.PrivilegedAction() {
0847: public Object run() {
0848: return System.getProperty(prop);
0849: }
0850: });
0851: }
0852:
0853: private static boolean getBooleanProperty(String prop,
0854: boolean defaultValue, String trueMsg, String falseMsg) {
0855: boolean value = defaultValue;
0856: String propValue = getProperty(prop);
0857:
0858: if (propValue != null) {
0859: value = Boolean.valueOf(propValue).booleanValue();
0860: System.err.println("Java 3D: "
0861: + (value ? trueMsg : falseMsg));
0862: }
0863: return value;
0864: }
0865:
0866: private static boolean getBooleanProperty(String prop,
0867: boolean defaultValue, String msg) {
0868: return getBooleanProperty(prop, defaultValue,
0869: (msg + " enabled"), (msg + " disabled"));
0870: }
0871:
0872: /**
0873: * Method to create and initialize the rendering Pipeline object,
0874: * and to load the native libraries needed by Java 3D. This is
0875: * called by the static initializer in VirtualUniverse <i>before</i>
0876: * the MasterControl object is created.
0877: */
0878: static void loadLibraries() {
0879: assert !librariesLoaded;
0880:
0881: // Get platform system properties
0882: String osName = getProperty("os.name").toLowerCase();
0883: String sunArchDataModel = getProperty("sun.arch.data.model");
0884:
0885: // Set global flags based on platform architecture
0886: isMacOs = osName != null && osName.startsWith("mac");
0887: isWindowsOs = osName != null && osName.startsWith("windows");
0888: boolean isWindowsVista = isWindowsOs
0889: && osName.indexOf("vista") != -1;
0890: boolean is64Bit = (sunArchDataModel != null)
0891: && sunArchDataModel.equals("64");
0892:
0893: // Issue 257: check to see if the sun.jnlp.applet.launcher property is set to true
0894: String sunAppletLauncher = getProperty("sun.jnlp.applet.launcher");
0895: appletLauncher = Boolean.valueOf(sunAppletLauncher);
0896:
0897: if (isCoreLoggable(Level.CONFIG)) {
0898: StringBuffer strBuf = new StringBuffer();
0899: strBuf.append("MasterControl.loadLibraries()\n").append(
0900: " osName [lower-case] = \"").append(osName)
0901: .append("\"").append(", sunArchDataModel = ")
0902: .append(sunArchDataModel).append("\n").append(
0903: " is64Bit = ").append(is64Bit).append(
0904: ", isWindowsOs = ").append(isWindowsOs)
0905: .append(", isMacOs = ").append(isMacOs).append(
0906: ", isWindowsVista = ").append(
0907: isWindowsVista);
0908: getCoreLogger().config(strBuf.toString());
0909: }
0910:
0911: // Initialize the Pipeline object associated with the
0912: // renderer specified by the "j3d.rend" system property.
0913: //
0914: // XXXX : We should consider adding support for a more flexible,
0915: // dynamic selection scheme via an API call.
0916:
0917: // Default rendering pipeline is the JOGL pipeline on MacOS and the
0918: // native OpenGL pipeline on all other platforms.
0919: Pipeline.Type pipelineType = isMacOs ? Pipeline.Type.JOGL
0920: : Pipeline.Type.NATIVE_OGL;
0921:
0922: final String rendStr = getProperty("j3d.rend");
0923: boolean nativeOglRequested = false;
0924: if (rendStr == null) {
0925: // Use default pipeline
0926: } else if (rendStr.equals("ogl") && !isMacOs) {
0927: pipelineType = Pipeline.Type.NATIVE_OGL;
0928: nativeOglRequested = true;
0929: } else if (rendStr.equals("d3d") && isWindowsOs) {
0930: pipelineType = Pipeline.Type.NATIVE_D3D;
0931: } else if (rendStr.equals("jogl")) {
0932: pipelineType = Pipeline.Type.JOGL;
0933: } else if (rendStr.equals("noop")) {
0934: pipelineType = Pipeline.Type.NOOP;
0935: } else {
0936: System.err.println("Java 3D: Unrecognized renderer: "
0937: + rendStr);
0938: // Use default pipeline
0939: }
0940:
0941: // Issue 452 : if we are on 32-bit Windows, then check whether we
0942: // can and should use OpenGL. Note that we can't do this on 64-bit
0943: // Windows until we have a 64-bit D3D pipeline.
0944: if (isWindowsOs && !is64Bit
0945: && pipelineType == Pipeline.Type.NATIVE_OGL) {
0946: if (!Pipeline.useNativeOgl(isWindowsVista,
0947: nativeOglRequested)) {
0948: pipelineType = Pipeline.Type.NATIVE_D3D;
0949: }
0950: }
0951:
0952: // Construct the singleton Pipeline instance
0953: Pipeline.createPipeline(pipelineType);
0954:
0955: // Get the global j3d.shadingLanguage system property
0956: final String slStr = getProperty("j3d.shadingLanguage");
0957: if (slStr != null) {
0958: boolean found = false;
0959: if (slStr.equals("GLSL")) {
0960: globalShadingLanguage = Shader.SHADING_LANGUAGE_GLSL;
0961: found = true;
0962: } else if (slStr.equals("Cg")) {
0963: globalShadingLanguage = Shader.SHADING_LANGUAGE_CG;
0964: found = true;
0965: }
0966:
0967: if (found) {
0968: System.err
0969: .println("Java 3D: Setting global shading language to "
0970: + slStr);
0971: } else {
0972: System.err
0973: .println("Java 3D: Unrecognized shading language: "
0974: + slStr);
0975: }
0976: }
0977:
0978: // Load all required libraries
0979: Pipeline.getPipeline().loadLibraries(globalShadingLanguage);
0980:
0981: // Check whether the Cg library is available
0982: if (globalShadingLanguage == Shader.SHADING_LANGUAGE_CG) {
0983: cgLibraryAvailable = Pipeline.getPipeline()
0984: .isCgLibraryAvailable();
0985: }
0986:
0987: // Check whether the GLSL library is available
0988: if (globalShadingLanguage == Shader.SHADING_LANGUAGE_GLSL) {
0989: glslLibraryAvailable = Pipeline.getPipeline()
0990: .isGLSLLibraryAvailable();
0991: }
0992:
0993: assert !(glslLibraryAvailable && cgLibraryAvailable) : "ERROR: cannot support both GLSL and CG at the same time";
0994:
0995: librariesLoaded = true;
0996: }
0997:
0998: /**
0999: * Invoke from InputDeviceScheduler to create an
1000: * InputDeviceBlockingThread.
1001: */
1002: InputDeviceBlockingThread getInputDeviceBlockingThread(
1003: final InputDevice device) {
1004:
1005: return (InputDeviceBlockingThread) java.security.AccessController
1006: .doPrivileged(new java.security.PrivilegedAction() {
1007: public Object run() {
1008: synchronized (rootThreadGroup) {
1009: Thread thread = new InputDeviceBlockingThread(
1010: rootThreadGroup, device);
1011: thread.setPriority(threadPriority);
1012: return thread;
1013: }
1014: }
1015: });
1016: }
1017:
1018: /**
1019: * Set thread priority to all threads under Java3D thread group.
1020: */
1021: void setThreadPriority(final int pri) {
1022: synchronized (rootThreadGroup) {
1023: threadPriority = pri;
1024: java.security.AccessController
1025: .doPrivileged(new java.security.PrivilegedAction() {
1026: public Object run() {
1027: Thread list[] = new Thread[rootThreadGroup
1028: .activeCount()];
1029: int count = rootThreadGroup.enumerate(list);
1030: for (int i = count - 1; i >= 0; i--) {
1031: list[i].setPriority(pri);
1032: }
1033: return null;
1034: }
1035: });
1036: }
1037: }
1038:
1039: /**
1040: * Return Java3D thread priority
1041: */
1042: int getThreadPriority() {
1043: return threadPriority;
1044: }
1045:
1046: /**
1047: * This returns the a unused renderer bit
1048: */
1049: int getRendererBit() {
1050: return (1 << rendererCount++);
1051: }
1052:
1053: /**
1054: * This returns the a unused renderer bit
1055: */
1056: int getRendererId() {
1057: return rendererCount++;
1058: }
1059:
1060: /**
1061: * This returns a context creation time stamp
1062: * Note: this has to be called under the contextCreationLock
1063: */
1064: long getContextTimeStamp() {
1065: return (++contextTimeStamp);
1066: }
1067:
1068: /**
1069: * This returns the a unused displayListId
1070: */
1071: Integer getDisplayListId() {
1072: return (Integer) FreeListManager
1073: .getObject(FreeListManager.DISPLAYLIST);
1074: }
1075:
1076: void freeDisplayListId(Integer id) {
1077: FreeListManager.freeObject(FreeListManager.DISPLAYLIST, id);
1078: }
1079:
1080: /**
1081: * This returns the a unused textureId
1082: */
1083: int getTexture2DId() {
1084: // MasterControl has to handle the ID itself. 2D and 3D ideas must
1085: // never be the same, so the counter has to be in the MasterControl
1086: MemoryFreeList textureIds = FreeListManager
1087: .getFreeList(FreeListManager.TEXTURE2D);
1088: int id;
1089:
1090: synchronized (textureIdLock) {
1091: if (textureIds.size() > 0) {
1092: id = ((Integer) FreeListManager
1093: .getObject(FreeListManager.TEXTURE2D))
1094: .intValue();
1095: } else {
1096: id = (++textureIdCount);
1097: }
1098: return id;
1099: }
1100: }
1101:
1102: int getTexture3DId() {
1103: // MasterControl has to handle the ID itself. 2D and 3D ideas must
1104: // never be the same, so the counter has to be in the MasterControl
1105: MemoryFreeList textureIds = FreeListManager
1106: .getFreeList(FreeListManager.TEXTURE3D);
1107: synchronized (textureIdLock) {
1108: if (textureIds.size > 0) {
1109: return ((Integer) FreeListManager
1110: .getObject(FreeListManager.TEXTURE3D))
1111: .intValue();
1112: } else
1113: return (++textureIdCount);
1114: }
1115: }
1116:
1117: void freeTexture2DId(int id) {
1118: FreeListManager.freeObject(FreeListManager.TEXTURE2D,
1119: new Integer(id));
1120: }
1121:
1122: void freeTexture3DId(int id) {
1123: FreeListManager.freeObject(FreeListManager.TEXTURE3D,
1124: new Integer(id));
1125: }
1126:
1127: int getCanvasId() {
1128: int i;
1129:
1130: synchronized (canvasIdLock) {
1131: // Master control need to keep count itself
1132: for (i = canvasFreeIndex; i < canvasIds.length; i++) {
1133: if (canvasIds[i] == false)
1134: break;
1135: }
1136:
1137: if (i >= canvasIds.length) {
1138: throw new RuntimeException(
1139: "Cannot render to more than 32 Canvas3Ds");
1140: }
1141:
1142: canvasIds[i] = true;
1143: canvasFreeIndex = i + 1;
1144: }
1145:
1146: return i;
1147:
1148: }
1149:
1150: void freeCanvasId(int canvasId) {
1151: // Valid range is [0, 31]
1152: synchronized (canvasIdLock) {
1153:
1154: canvasIds[canvasId] = false;
1155: if (canvasFreeIndex > canvasId) {
1156: canvasFreeIndex = canvasId;
1157: }
1158: }
1159: }
1160:
1161: /**
1162: * Create a Renderer if it is not already done so.
1163: * This is used for GraphicsConfigTemplate3D passing
1164: * graphics call to RequestRenderer, and for creating
1165: * an off-screen buffer for an off-screen Canvas3D.
1166: */
1167: private Renderer createRenderer(GraphicsConfiguration gc) {
1168: final GraphicsDevice gd = gc.getDevice();
1169:
1170: Renderer rdr = (Renderer) Screen3D.deviceRendererMap.get(gd);
1171: if (rdr != null) {
1172: return rdr;
1173: }
1174:
1175: java.security.AccessController
1176: .doPrivileged(new java.security.PrivilegedAction() {
1177: public Object run() {
1178: Renderer r;
1179: synchronized (rootThreadGroup) {
1180: r = new Renderer(rootThreadGroup);
1181: r.initialize();
1182: r.setPriority(threadPriority);
1183: Screen3D.deviceRendererMap.put(gd, r);
1184: }
1185: return null;
1186: }
1187: });
1188:
1189: threadListsChanged = true;
1190:
1191: return (Renderer) Screen3D.deviceRendererMap.get(gd);
1192: }
1193:
1194: /**
1195: * Post the request in queue
1196: */
1197: void postRequest(Integer type, Object obj) {
1198:
1199: synchronized (mcThreadLock) {
1200: synchronized (requestObjList) {
1201: if (mcThread == null) {
1202: if ((type == ACTIVATE_VIEW)
1203: || (type == GETBESTCONFIG)
1204: || (type == SET_VIEW)
1205: || (type == ISCONFIGSUPPORT)
1206: || (type == SET_QUERYPROPERTIES)
1207: || (type == SET_GRAPHICSCONFIG_FEATURES)) {
1208: createMasterControlThread();
1209: requestObjList.add(obj);
1210: requestTypeList.add(type);
1211: pendingRequest = true;
1212: } else if (type == EMPTY_UNIVERSE) {
1213: destroyUniverseThreads((VirtualUniverse) obj);
1214: } else if (type == STOP_VIEW) {
1215: View v = (View) obj;
1216: v.stopViewCount = -1;
1217: v.isRunning = false;
1218: } else if (type == STOP_RENDERER) {
1219: if (obj instanceof Canvas3D) {
1220: ((Canvas3D) obj).isRunningStatus = false;
1221: } else {
1222: ((Renderer) obj).userStop = true;
1223: }
1224: } else if (type == UNREGISTER_VIEW) {
1225: ((View) obj).doneUnregister = true;
1226: } else {
1227: requestObjList.add(obj);
1228: requestTypeList.add(type);
1229: pendingRequest = true;
1230: }
1231: } else {
1232: requestObjList.add(obj);
1233: requestTypeList.add(type);
1234: pendingRequest = true;
1235: }
1236: }
1237: }
1238:
1239: setWork();
1240: }
1241:
1242: /**
1243: * This procedure is invoked when isRunning is false.
1244: * Return true when there is no more pending request so that
1245: * Thread can terminate. Otherwise we have to recreate
1246: * the MC related threads.
1247: */
1248: boolean mcThreadDone() {
1249: synchronized (mcThreadLock) {
1250: synchronized (requestObjList) {
1251: if (!pendingRequest) {
1252: mcThread = null;
1253: if (renderingAttributesStructure.updateThread != null) {
1254: renderingAttributesStructure.updateThread
1255: .finish();
1256: renderingAttributesStructure.updateThread = null;
1257: }
1258: renderingAttributesStructure = new RenderingAttributesStructure();
1259: if (timerThread != null) {
1260: timerThread.finish();
1261: timerThread = null;
1262: }
1263: if (notificationThread != null) {
1264: notificationThread.finish();
1265: notificationThread = null;
1266: }
1267: requestObjList.clear();
1268: requestTypeList.clear();
1269: return true;
1270: }
1271: running = true;
1272: createMCThreads();
1273: return false;
1274: }
1275: }
1276: }
1277:
1278: /**
1279: * Returns whether we are using D3D.
1280: * TODO: most code that cares about this should move into the pipeline
1281: */
1282: final boolean isD3D() {
1283: return Pipeline.getPipeline().getPipelineType() == Pipeline.Type.NATIVE_D3D;
1284: }
1285:
1286: /**
1287: * Returns whether we are running on Windows
1288: * TODO: most code that cares about this should move into the pipeline
1289: */
1290: static final boolean isWindows() {
1291: return isWindowsOs;
1292: }
1293:
1294: /**
1295: * Returns a flag indicating whether the sun.jnlp.applet.launcher system
1296: * property is set to true.
1297: */
1298: static boolean isAppletLauncher() {
1299: return appletLauncher;
1300: }
1301:
1302: /**
1303: * This method increments and returns the next time value
1304: * timeLock must get before this procedure is invoked
1305: */
1306: final long getTime() {
1307: return (time++);
1308: }
1309:
1310: /**
1311: * This takes a given message and parses it out to the structures and
1312: * marks its time value.
1313: */
1314: void processMessage(J3dMessage message) {
1315:
1316: synchronized (timeLock) {
1317: message.time = getTime();
1318: sendMessage(message);
1319: }
1320: setWork();
1321: }
1322:
1323: /**
1324: * This takes an array of messages and parses them out to the structures and
1325: * marks the time value. Make sure, setWork() is done at the very end
1326: * to make sure all the messages will be processed in the same frame
1327: */
1328: void processMessage(J3dMessage[] messages) {
1329:
1330: synchronized (timeLock) {
1331: long time = getTime();
1332:
1333: for (int i = 0; i < messages.length; i++) {
1334: messages[i].time = time;
1335: sendMessage(messages[i]);
1336: }
1337: }
1338: setWork();
1339: }
1340:
1341: /**
1342: * This takes the specified notification message and sends it to the
1343: * notification thread for processing.
1344: */
1345: void sendNotification(J3dNotification notification) {
1346: notificationThread.addNotification(notification);
1347: }
1348:
1349: /**
1350: * Create and start the MasterControl Thread.
1351: */
1352: void createMasterControlThread() {
1353: // Issue 364: don't create master control thread if already created
1354: if (mcThread != null) {
1355: return;
1356: }
1357:
1358: running = true;
1359: workToDo = true;
1360: state = RUNNING;
1361: java.security.AccessController
1362: .doPrivileged(new java.security.PrivilegedAction() {
1363: public Object run() {
1364: synchronized (rootThreadGroup) {
1365: mcThread = new MasterControlThread(
1366: rootThreadGroup);
1367: mcThread.setPriority(threadPriority);
1368: }
1369: return null;
1370: }
1371: });
1372: }
1373:
1374: // assuming the timeLock is already acquired
1375:
1376: /**
1377: * Send a message to another Java 3D thread.
1378: */
1379: void sendMessage(J3dMessage message) {
1380:
1381: synchronized (message) {
1382: VirtualUniverse u = message.universe;
1383: int targetThreads = message.threads;
1384:
1385: if (isCoreLoggable(Level.FINEST)) {
1386: dumpMessage("sendMessage", message);
1387: }
1388:
1389: if ((targetThreads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
1390: renderingAttributesStructure.addMessage(message);
1391: }
1392:
1393: // GraphicsContext3D send message with universe = null
1394: if (u != null) {
1395: if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
1396: u.geometryStructure.addMessage(message);
1397: }
1398: if ((targetThreads & J3dThread.UPDATE_TRANSFORM) != 0) {
1399: u.transformStructure.addMessage(message);
1400: }
1401: if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
1402: u.behaviorStructure.addMessage(message);
1403: }
1404: if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
1405: u.soundStructure.addMessage(message);
1406: }
1407: if ((targetThreads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
1408: u.renderingEnvironmentStructure.addMessage(message);
1409: }
1410: }
1411:
1412: if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
1413: // Note that we don't check for active view
1414: if (message.view != null
1415: && message.view.soundScheduler != null) {
1416: // This make sure that message won't lost even
1417: // though this view not yet register
1418: message.view.soundScheduler.addMessage(message);
1419: } else {
1420: synchronized (views) {
1421: View v[] = (View[]) views.toArray(false);
1422: int i = views.arraySize() - 1;
1423: if (u == null) {
1424: while (i >= 0) {
1425: v[i--].soundScheduler
1426: .addMessage(message);
1427: }
1428: } else {
1429: while (i >= 0) {
1430: if (v[i].universe == u) {
1431: v[i].soundScheduler
1432: .addMessage(message);
1433: }
1434: i--;
1435: }
1436: }
1437: }
1438: }
1439: }
1440:
1441: if ((targetThreads & J3dThread.UPDATE_RENDER) != 0) {
1442: // Note that we don't check for active view
1443: if (message.view != null
1444: && message.view.renderBin != null) {
1445: // This make sure that message won't lost even
1446: // though this view not yet register
1447: message.view.renderBin.addMessage(message);
1448: } else {
1449: synchronized (views) {
1450: View v[] = (View[]) views.toArray(false);
1451: int i = views.arraySize() - 1;
1452: if (u == null) {
1453: while (i >= 0) {
1454: v[i--].renderBin.addMessage(message);
1455: }
1456: } else {
1457: while (i >= 0) {
1458: if (v[i].universe == u) {
1459: v[i].renderBin.addMessage(message);
1460: }
1461: i--;
1462: }
1463: }
1464: }
1465: }
1466: }
1467:
1468: if (message.getRefcount() == 0) {
1469: message.clear();
1470: }
1471: }
1472: }
1473:
1474: /**
1475: * Send a message to another Java 3D thread.
1476: * This variant is only call by TimerThread for Input Device Scheduler
1477: * or to redraw all View for RenderThread
1478: */
1479: void sendRunMessage(int targetThreads) {
1480:
1481: synchronized (timeLock) {
1482:
1483: long time = getTime();
1484:
1485: if ((targetThreads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
1486: synchronized (inputDeviceThreads) {
1487: InputDeviceScheduler ds[] = (InputDeviceScheduler[]) inputDeviceThreads
1488: .toArray(false);
1489: for (int i = inputDeviceThreads.size() - 1; i >= 0; i--) {
1490: if (ds[i].physicalEnv.activeViewRef > 0) {
1491: ds[i].getThreadData().lastUpdateTime = time;
1492: }
1493: }
1494:
1495: // timerThread instance in MC will set to null in
1496: // destroyUniverseThreads() so we need to check if
1497: // TimerThread kick in to sendRunMessage() after that.
1498: // It happens because TimerThread is the only thread run
1499: // asychronizously with MasterControl thread.
1500:
1501: if (timerThread != null) {
1502: // Notify TimerThread to wakeup this procedure
1503: // again next time.
1504: timerThread.addInputDeviceSchedCond();
1505: }
1506: }
1507: }
1508: if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
1509: synchronized (renderThreadData) {
1510: J3dThreadData[] threads = (J3dThreadData[]) renderThreadData
1511: .toArray(false);
1512: int i = renderThreadData.arraySize() - 1;
1513: J3dThreadData thr;
1514: while (i >= 0) {
1515: thr = threads[i--];
1516: if (thr.view.renderBinReady) {
1517: thr.lastUpdateTime = time;
1518: }
1519: }
1520: }
1521: }
1522: }
1523: setWork();
1524: }
1525:
1526: /**
1527: * Send a message to another Java 3D thread.
1528: * This variant is only call by TimerThread for Sound Scheduler
1529: */
1530: void sendRunMessage(long waitTime, View view, int targetThreads) {
1531:
1532: synchronized (timeLock) {
1533:
1534: long time = getTime();
1535:
1536: if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
1537: if (view.soundScheduler != null) {
1538: view.soundScheduler.threadData.lastUpdateTime = time;
1539: }
1540: // wakeup this procedure next time
1541: // QUESTION: waitTime calculated some milliseconds BEFORE
1542: // this methods getTime() called - shouldn't actual
1543: // sound Complete time be passed by SoundScheduler
1544: // QUESTION: will this wake up only soundScheduler associated
1545: // with this view?? (since only it's lastUpdateTime is set)
1546: // or all soundSchedulers??
1547: timerThread.addSoundSchedCond(time + waitTime);
1548: }
1549: }
1550: setWork();
1551: }
1552:
1553: /**
1554: * Send a message to another Java 3D thread.
1555: * This variant is only called to update Render Thread
1556: */
1557: void sendRunMessage(View v, int targetThreads) {
1558:
1559: synchronized (timeLock) {
1560: long time = getTime();
1561:
1562: if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
1563: synchronized (renderThreadData) {
1564: J3dThreadData[] threads = (J3dThreadData[]) renderThreadData
1565: .toArray(false);
1566: int i = renderThreadData.arraySize() - 1;
1567: J3dThreadData thr;
1568: while (i >= 0) {
1569: thr = threads[i--];
1570: if (thr.view == v && v.renderBinReady) {
1571: thr.lastUpdateTime = time;
1572: }
1573: }
1574: }
1575: }
1576: }
1577: setWork();
1578: }
1579:
1580: /**
1581: * This sends a run message to the given threads.
1582: */
1583: void sendRunMessage(VirtualUniverse u, int targetThreads) {
1584: // We don't sendRunMessage to update structure except Behavior
1585:
1586: synchronized (timeLock) {
1587: long time = getTime();
1588:
1589: if ((targetThreads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
1590: if (u.behaviorScheduler != null) {
1591: u.behaviorScheduler.getThreadData(null, null).lastUpdateTime = time;
1592: }
1593: }
1594:
1595: if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
1596: u.behaviorStructure.threadData.lastUpdateTime = time;
1597: }
1598:
1599: if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
1600: u.geometryStructure.threadData.lastUpdateTime = time;
1601: }
1602:
1603: if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
1604: u.soundStructure.threadData.lastUpdateTime = time;
1605: }
1606:
1607: if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
1608: synchronized (views) {
1609: View v[] = (View[]) views.toArray(false);
1610: for (int i = views.arraySize() - 1; i >= 0; i--) {
1611: if ((v[i].soundScheduler != null)
1612: && (v[i].universe == u)) {
1613: v[i].soundScheduler.threadData.lastUpdateTime = time;
1614: }
1615: }
1616: }
1617: }
1618:
1619: if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
1620:
1621: synchronized (renderThreadData) {
1622: J3dThreadData[] threads = (J3dThreadData[]) renderThreadData
1623: .toArray(false);
1624: int i = renderThreadData.arraySize() - 1;
1625: J3dThreadData thr;
1626: while (i >= 0) {
1627: thr = threads[i--];
1628: if (thr.view.universe == u
1629: && thr.view.renderBinReady) {
1630: thr.lastUpdateTime = time;
1631: }
1632: }
1633: }
1634: }
1635: }
1636:
1637: setWork();
1638: }
1639:
1640: /**
1641: * Return a clone of View, we can't access
1642: * individual element of View after getting the size
1643: * in separate API call without synchronized views.
1644: */
1645: UnorderList cloneView() {
1646: return (UnorderList) views.clone();
1647: }
1648:
1649: /**
1650: * Return true if view is already registered with MC
1651: */
1652: boolean isRegistered(View view) {
1653: return views.contains(view);
1654: }
1655:
1656: /**
1657: * This snapshots the time values to be used for this iteration.
1658: * Note that this method is called without the timeLock held.
1659: * We must synchronize on timeLock to prevent updating
1660: * thread.lastUpdateTime from user thread in sendMessage()
1661: * or sendRunMessage().
1662: */
1663: private void updateTimeValues() {
1664: synchronized (timeLock) {
1665: int i = 0;
1666: J3dThreadData lastThread = null;
1667: J3dThreadData thread = null;
1668: long lastTime = currentTime;
1669:
1670: currentTime = getTime();
1671:
1672: J3dThreadData threads[] = (J3dThreadData[]) stateWorkThreads
1673: .toArray(false);
1674: int size = stateWorkThreads.arraySize();
1675:
1676: while (i < lastTransformStructureThread) {
1677: thread = threads[i++];
1678:
1679: if ((thread.lastUpdateTime > thread.lastRunTime)
1680: && !thread.thread.userStop) {
1681: lastThread = thread;
1682: thread.needsRun = true;
1683: thread.threadOpts = J3dThreadData.CONT_THREAD;
1684: thread.lastRunTime = currentTime;
1685: } else {
1686: thread.needsRun = false;
1687: }
1688: }
1689:
1690: if (lastThread != null) {
1691: lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
1692: lastThread = null;
1693: }
1694:
1695: while (i < lastStructureUpdateThread) {
1696: thread = threads[i++];
1697: if ((thread.lastUpdateTime > thread.lastRunTime)
1698: && !thread.thread.userStop) {
1699: lastThread = thread;
1700: thread.needsRun = true;
1701: thread.threadOpts = J3dThreadData.CONT_THREAD;
1702: thread.lastRunTime = currentTime;
1703: } else {
1704: thread.needsRun = false;
1705: }
1706: }
1707: if (lastThread != null) {
1708: lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
1709: lastThread = null;
1710: }
1711:
1712: while (i < size) {
1713: thread = threads[i++];
1714: if ((thread.lastUpdateTime > thread.lastRunTime)
1715: && !thread.thread.userStop) {
1716: lastThread = thread;
1717: thread.needsRun = true;
1718: thread.threadOpts = J3dThreadData.CONT_THREAD;
1719: thread.lastRunTime = currentTime;
1720: } else {
1721: thread.needsRun = false;
1722: }
1723: }
1724: if (lastThread != null) {
1725: lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
1726: lastThread = null;
1727: }
1728:
1729: threads = (J3dThreadData[]) renderWorkThreads
1730: .toArray(false);
1731: size = renderWorkThreads.arraySize();
1732: View v;
1733: J3dThreadData lastRunThread = null;
1734: waitTimestamp++;
1735: sleepTime = 0L;
1736:
1737: boolean threadToRun = false; // Not currently used
1738:
1739: // Fix for Issue 12: loop through the list of threads, calling
1740: // computeCycleTime() exactly once per view. This ensures that
1741: // all threads for a given view see consistent values for
1742: // isMinCycleTimeAchieve and sleepTime.
1743: v = null;
1744: for (i = 0; i < size; i++) {
1745: thread = threads[i];
1746: if (thread.view != v) {
1747: thread.view.computeCycleTime();
1748: // Set sleepTime to the value needed to satify the
1749: // minimum cycle time of the slowest view
1750: if (thread.view.sleepTime > sleepTime) {
1751: sleepTime = thread.view.sleepTime;
1752: }
1753: }
1754: v = thread.view;
1755: }
1756:
1757: v = null;
1758: for (i = 0; i < size; i++) {
1759: thread = threads[i];
1760: if (thread.canvas == null) { // Only for swap thread
1761: ((Object[]) thread.threadArgs)[3] = null;
1762: }
1763: if ((thread.lastUpdateTime > thread.lastRunTime)
1764: && !thread.thread.userStop) {
1765:
1766: if (thread.thread.lastWaitTimestamp == waitTimestamp) {
1767: // This renderer thread is repeated. We must wait
1768: // until all previous renderer threads done before
1769: // allowing this thread to continue. Note that
1770: // lastRunThread can't be null in this case.
1771: waitTimestamp++;
1772: if (thread.view != v) {
1773: // A new View is start
1774: v = thread.view;
1775: threadToRun = true;
1776: lastRunThread.threadOpts = (J3dThreadData.STOP_TIMER | J3dThreadData.WAIT_ALL_THREADS);
1777: ((Object[]) lastRunThread.threadArgs)[3] = lastRunThread.view;
1778: thread.threadOpts = (J3dThreadData.START_TIMER | J3dThreadData.CONT_THREAD);
1779: } else {
1780: if ((lastRunThread.threadOpts & J3dThreadData.START_TIMER) != 0) {
1781: lastRunThread.threadOpts = (J3dThreadData.START_TIMER | J3dThreadData.WAIT_ALL_THREADS);
1782:
1783: } else {
1784: lastRunThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
1785: }
1786: thread.threadOpts = J3dThreadData.CONT_THREAD;
1787:
1788: }
1789: } else {
1790: if (thread.view != v) {
1791: v = thread.view;
1792: threadToRun = true;
1793: // Although the renderer thread is not
1794: // repeated. We still need to wait all
1795: // previous renderer threads if new View
1796: // start.
1797: if (lastRunThread != null) {
1798: lastRunThread.threadOpts = (J3dThreadData.STOP_TIMER | J3dThreadData.WAIT_ALL_THREADS);
1799: ((Object[]) lastRunThread.threadArgs)[3] = lastRunThread.view;
1800: }
1801: thread.threadOpts = (J3dThreadData.START_TIMER | J3dThreadData.CONT_THREAD);
1802: } else {
1803: thread.threadOpts = J3dThreadData.CONT_THREAD;
1804: }
1805: }
1806: thread.thread.lastWaitTimestamp = waitTimestamp;
1807: thread.needsRun = true;
1808: thread.lastRunTime = currentTime;
1809: lastRunThread = thread;
1810: } else {
1811: thread.needsRun = false;
1812: }
1813: }
1814:
1815: if (lastRunThread != null) {
1816: lastRunThread.threadOpts = (J3dThreadData.STOP_TIMER
1817: | J3dThreadData.WAIT_ALL_THREADS | J3dThreadData.LAST_STOP_TIMER);
1818: lockGeometry = true;
1819: ((Object[]) lastRunThread.threadArgs)[3] = lastRunThread.view;
1820: } else {
1821: lockGeometry = false;
1822: }
1823: }
1824:
1825: // Issue 275 - go to sleep without holding timeLock
1826: // Sleep for the amount of time needed to satisfy the minimum
1827: // cycle time for all views.
1828: if (sleepTime > 0) {
1829: // System.err.println("MasterControl: sleep(" + sleepTime + ")");
1830: try {
1831: Thread.sleep(sleepTime);
1832: } catch (InterruptedException e) {
1833: System.err.println(e);
1834: }
1835: // System.err.println("MasterControl: done sleeping");
1836: }
1837: }
1838:
1839: private void createUpdateThread(J3dStructure structure) {
1840: final J3dStructure s = structure;
1841:
1842: if (s.updateThread == null) {
1843: java.security.AccessController
1844: .doPrivileged(new java.security.PrivilegedAction() {
1845: public Object run() {
1846: synchronized (rootThreadGroup) {
1847: s.updateThread = new StructureUpdateThread(
1848: rootThreadGroup, s,
1849: s.threadType);
1850: s.updateThread
1851: .setPriority(threadPriority);
1852: }
1853: return null;
1854: }
1855: });
1856: s.updateThread.initialize();
1857: s.threadData.thread = s.updateThread;
1858: // This takes into accout for thread that just destroy and
1859: // create again. In this case the threadData may receive
1860: // message before the thread actually created. We don't want
1861: // the currentTime to overwrite the update time of which
1862: // is set by threadData when get message.
1863: s.threadData.lastUpdateTime = Math.max(currentTime,
1864: s.threadData.lastUpdateTime);
1865: }
1866: }
1867:
1868: private void emptyMessageList(J3dStructure structure, View v) {
1869: if (structure != null) {
1870: if (v == null) {
1871: if (structure.threadData != null) {
1872: structure.threadData.thread = null;
1873: }
1874:
1875: if (structure.updateThread != null) {
1876: structure.updateThread.structure = null;
1877: }
1878: structure.updateThread = null;
1879: }
1880: boolean otherViewExist = false;
1881: if ((v != null) && (v.universe != null)) {
1882: // Check if there is any other View register with the
1883: // same universe
1884: for (int i = views.size() - 1; i >= 0; i--) {
1885: if (((View) views.get(i)).universe == v.universe) {
1886: otherViewExist = true;
1887: break;
1888: }
1889: }
1890: }
1891:
1892: UnorderList mlist = structure.messageList;
1893: // Note that message is add at the end of array
1894: synchronized (mlist) {
1895: int size = mlist.size();
1896: if (size > 0) {
1897: J3dMessage mess[] = (J3dMessage[]) mlist
1898: .toArray(false);
1899: J3dMessage m;
1900: int i = 0;
1901:
1902: Object oldRef = null;
1903: while (i < size) {
1904: m = mess[i];
1905: if ((v == null)
1906: || (m.view == v)
1907: || ((m.view == null) && !otherViewExist)) {
1908: if (m.type == J3dMessage.INSERT_NODES) {
1909: // There is another View register request
1910: // immediately following, so no need
1911: // to remove message.
1912: break;
1913: }
1914: // Some other thread may still using this
1915: // message so we should not directly
1916: // add this message to free lists
1917: m.decRefcount();
1918: mlist.removeOrdered(i);
1919: size--;
1920: } else {
1921: i++;
1922: }
1923: }
1924: }
1925: }
1926: }
1927: }
1928:
1929: private void destroyUpdateThread(J3dStructure structure) {
1930: // If unregisterView message got before EMPTY_UNIVERSE
1931: // message, then updateThread is already set to null.
1932: if (structure.updateThread != null) {
1933: structure.updateThread.finish();
1934: structure.updateThread.structure = null;
1935: structure.updateThread = null;
1936: }
1937: structure.threadData.thread = null;
1938: structure.clearMessages();
1939: }
1940:
1941: /**
1942: * This register a View with MasterControl.
1943: * The View has at least one Canvas3D added to a container.
1944: */
1945: private void registerView(View v) {
1946: final VirtualUniverse univ = v.universe;
1947:
1948: if (views.contains(v) && regUniverseList.contains(univ)) {
1949: return; // already register
1950: }
1951:
1952: if (timerThread == null) {
1953: // This handle the case when MC shutdown and restart in
1954: // a series of pending request
1955: running = true;
1956: createMCThreads();
1957: }
1958: // If viewId is null, assign one ..
1959: v.assignViewId();
1960:
1961: // Create thread if not done before
1962: createUpdateThread(univ.behaviorStructure);
1963: createUpdateThread(univ.geometryStructure);
1964: createUpdateThread(univ.soundStructure);
1965: createUpdateThread(univ.renderingEnvironmentStructure);
1966: createUpdateThread(univ.transformStructure);
1967:
1968: // create Behavior scheduler
1969: J3dThreadData threadData = null;
1970:
1971: if (univ.behaviorScheduler == null) {
1972: java.security.AccessController
1973: .doPrivileged(new java.security.PrivilegedAction() {
1974: public Object run() {
1975: synchronized (rootThreadGroup) {
1976: univ.behaviorScheduler = new BehaviorScheduler(
1977: rootThreadGroup, univ);
1978: univ.behaviorScheduler
1979: .setPriority(threadPriority);
1980: }
1981: return null;
1982: }
1983: });
1984: univ.behaviorScheduler.initialize();
1985: univ.behaviorScheduler.userStop = v.stopBehavior;
1986: threadData = univ.behaviorScheduler.getThreadData(null,
1987: null);
1988: threadData.thread = univ.behaviorScheduler;
1989: threadData.threadType = J3dThread.BEHAVIOR_SCHEDULER;
1990: threadData.lastUpdateTime = Math.max(currentTime,
1991: threadData.lastUpdateTime);
1992: }
1993:
1994: createUpdateThread(v.renderBin);
1995: createUpdateThread(v.soundScheduler);
1996:
1997: if (v.physicalEnvironment != null) {
1998: v.physicalEnvironment.addUser(v);
1999: }
2000: // create InputDeviceScheduler
2001: evaluatePhysicalEnv(v);
2002:
2003: regUniverseList.addUnique(univ);
2004: views.addUnique(v);
2005: }
2006:
2007: /**
2008: * This unregister a View with MasterControl.
2009: * The View no longer has any Canvas3Ds in a container.
2010: */
2011: private void unregisterView(View v) {
2012:
2013: if (!views.remove(v)) {
2014: v.active = false;
2015: v.doneUnregister = true;
2016: return; // already unregister
2017: }
2018:
2019: if (v.active) {
2020: viewDeactivate(v);
2021: }
2022:
2023: if (J3dDebug.devPhase) {
2024: J3dDebug
2025: .doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
2026: "MC: Destroy Sound Scheduler and RenderBin Update thread");
2027: }
2028:
2029: v.soundScheduler.updateThread.finish();
2030: v.renderBin.updateThread.finish();
2031: v.soundScheduler.updateThread = null;
2032: v.renderBin.updateThread = null;
2033:
2034: // remove VirtualUniverse related threads if Universe
2035: // is empty
2036: VirtualUniverse univ = v.universe;
2037: int i;
2038:
2039: synchronized (timeLock) {
2040: // The reason we need to sync. with timeLock is because we
2041: // don't want user thread running sendMessage() to
2042: // dispatch it in different structure queue when
2043: // part of the structure list is empty at the same time.
2044: // This will cause inconsistence in the message reference
2045: // count.
2046: emptyMessageList(v.soundScheduler, v);
2047: emptyMessageList(v.renderBin, v);
2048:
2049: if (univ.isEmpty()) {
2050: destroyUniverseThreads(univ);
2051: } else {
2052: emptyMessageList(univ.behaviorStructure, v);
2053: emptyMessageList(univ.geometryStructure, v);
2054: emptyMessageList(univ.soundStructure, v);
2055: emptyMessageList(univ.renderingEnvironmentStructure, v);
2056: emptyMessageList(univ.transformStructure, v);
2057: }
2058: }
2059:
2060: if (v.physicalEnvironment != null) {
2061: v.physicalEnvironment.removeUser(v);
2062: }
2063:
2064: // remove all InputDeviceScheduler if this is the last View
2065: UnorderList list = new UnorderList(1, PhysicalEnvironment.class);
2066: for (Enumeration e = PhysicalEnvironment.physicalEnvMap.keys(); e
2067: .hasMoreElements();) {
2068: PhysicalEnvironment phyEnv = (PhysicalEnvironment) e
2069: .nextElement();
2070: InputDeviceScheduler sched = (InputDeviceScheduler) PhysicalEnvironment.physicalEnvMap
2071: .get(phyEnv);
2072: for (i = phyEnv.users.size() - 1; i >= 0; i--) {
2073: if (views.contains((View) phyEnv.users.get(i))) {
2074: // at least one register view refer to it.
2075: break;
2076: }
2077: }
2078: if (i < 0) {
2079: if (J3dDebug.devPhase) {
2080: J3dDebug.doDebug(J3dDebug.masterControl,
2081: J3dDebug.LEVEL_1,
2082: "MC: Destroy InputDeviceScheduler thread "
2083: + sched);
2084: }
2085: sched.finish();
2086: phyEnv.inputsched = null;
2087: list.add(phyEnv);
2088: }
2089: }
2090: for (i = list.size() - 1; i >= 0; i--) {
2091: PhysicalEnvironment.physicalEnvMap.remove(list.get(i));
2092: }
2093:
2094: freeContext(v);
2095:
2096: if (views.isEmpty()) {
2097: if (J3dDebug.devPhase) {
2098: J3dDebug.doDebug(J3dDebug.masterControl,
2099: J3dDebug.LEVEL_1, "MC: Destroy all Renderers");
2100: }
2101: // remove all Renderers if this is the last View
2102: for (Enumeration e = Screen3D.deviceRendererMap.elements(); e
2103: .hasMoreElements();) {
2104: Renderer rdr = (Renderer) e.nextElement();
2105: Screen3D scr;
2106:
2107: rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
2108: runMonitor(RUN_RENDERER_CLEANUP, null, null, null, rdr);
2109: scr = rdr.onScreen;
2110: if (scr != null) {
2111: if (scr.renderer != null) {
2112: rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
2113: runMonitor(RUN_RENDERER_CLEANUP, null, null,
2114: null, scr.renderer);
2115: scr.renderer = null;
2116: }
2117:
2118: }
2119: scr = rdr.offScreen;
2120: if (scr != null) {
2121: if (scr.renderer != null) {
2122: rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
2123: runMonitor(RUN_RENDERER_CLEANUP, null, null,
2124: null, scr.renderer);
2125: scr.renderer = null;
2126: }
2127: }
2128: rdr.onScreen = null;
2129: rdr.offScreen = null;
2130: }
2131:
2132: // cleanup ThreadData corresponds to the view in renderer
2133: for (Enumeration e = Screen3D.deviceRendererMap.elements(); e
2134: .hasMoreElements();) {
2135: Renderer rdr = (Renderer) e.nextElement();
2136: rdr.cleanup();
2137: }
2138: // We have to reuse renderer even though MC exit
2139: // see bug 4363279
2140: // Screen3D.deviceRendererMap.clear();
2141:
2142: } else {
2143: // cleanup ThreadData corresponds to the view in renderer
2144: for (Enumeration e = Screen3D.deviceRendererMap.elements(); e
2145: .hasMoreElements();) {
2146: Renderer rdr = (Renderer) e.nextElement();
2147: rdr.cleanupView();
2148: }
2149: }
2150:
2151: freeMessageList.add(univ);
2152: freeMessageList.add(v);
2153:
2154: evaluateAllCanvases();
2155: stateWorkThreads.clear();
2156: renderWorkThreads.clear();
2157: requestRenderWorkThreads.clear();
2158: threadListsChanged = true;
2159:
2160: // This notify VirtualUniverse waitForMC() thread to continue
2161: v.doneUnregister = true;
2162: }
2163:
2164: /**
2165: * This procedure create MC thread that start together with MC.
2166: */
2167: void createMCThreads() {
2168:
2169: // There is only one renderingAttributesUpdate Thread globally
2170: createUpdateThread(renderingAttributesStructure);
2171:
2172: // Create timer thread
2173: java.security.AccessController
2174: .doPrivileged(new java.security.PrivilegedAction() {
2175: public Object run() {
2176: synchronized (rootThreadGroup) {
2177: timerThread = new TimerThread(
2178: rootThreadGroup);
2179: timerThread.setPriority(threadPriority);
2180: }
2181: return null;
2182: }
2183: });
2184: timerThread.start();
2185:
2186: // Create notification thread
2187: java.security.AccessController
2188: .doPrivileged(new java.security.PrivilegedAction() {
2189: public Object run() {
2190: synchronized (rootThreadGroup) {
2191: notificationThread = new NotificationThread(
2192: rootThreadGroup);
2193: notificationThread
2194: .setPriority(threadPriority);
2195: }
2196: return null;
2197: }
2198: });
2199: notificationThread.start();
2200: }
2201:
2202: /**
2203: * Destroy all VirtualUniverse related threads.
2204: * This procedure may call two times when Locale detach in a
2205: * live viewPlatform.
2206: */
2207: private void destroyUniverseThreads(VirtualUniverse univ) {
2208:
2209: if (regUniverseList.contains(univ)) {
2210: if (J3dDebug.devPhase) {
2211: J3dDebug.doDebug(J3dDebug.masterControl,
2212: J3dDebug.LEVEL_1,
2213: "MC: Destroy universe threads " + univ);
2214: }
2215: destroyUpdateThread(univ.behaviorStructure);
2216: destroyUpdateThread(univ.geometryStructure);
2217: destroyUpdateThread(univ.soundStructure);
2218: destroyUpdateThread(univ.renderingEnvironmentStructure);
2219: destroyUpdateThread(univ.transformStructure);
2220: univ.behaviorScheduler.finish();
2221: univ.behaviorScheduler.free();
2222: univ.behaviorScheduler = null;
2223: univ.initMCStructure();
2224: activeUniverseList.remove(univ);
2225: regUniverseList.remove(univ);
2226: } else {
2227: emptyMessageList(univ.behaviorStructure, null);
2228: emptyMessageList(univ.geometryStructure, null);
2229: emptyMessageList(univ.soundStructure, null);
2230: emptyMessageList(univ.renderingEnvironmentStructure, null);
2231: emptyMessageList(univ.transformStructure, null);
2232: }
2233:
2234: if (regUniverseList.isEmpty() && views.isEmpty()) {
2235: if (J3dDebug.devPhase) {
2236: J3dDebug
2237: .doDebug(J3dDebug.masterControl,
2238: J3dDebug.LEVEL_1,
2239: "MC: Destroy RenderingAttributes Update and Timer threads");
2240: }
2241: if (renderingAttributesStructure.updateThread != null) {
2242: renderingAttributesStructure.updateThread.finish();
2243: renderingAttributesStructure.updateThread = null;
2244: }
2245: renderingAttributesStructure.messageList.clear();
2246: renderingAttributesStructure.objList = new ArrayList();
2247: renderingAttributesStructure = new RenderingAttributesStructure();
2248: if (timerThread != null) {
2249: timerThread.finish();
2250: timerThread = null;
2251: }
2252: if (notificationThread != null) {
2253: notificationThread.finish();
2254: notificationThread = null;
2255: }
2256:
2257: // shouldn't all of these be synchronized ???
2258: synchronized (VirtualUniverse.mc.deviceScreenMap) {
2259: deviceScreenMap.clear();
2260: }
2261:
2262: mirrorObjects.clear();
2263: // Note: We should not clear the DISPLAYLIST/TEXTURE
2264: // list here because other structure may release them
2265: // later
2266:
2267: for (int i = 0; i < canvasIds.length; i++) {
2268: canvasIds[i] = false;
2269: }
2270: canvasFreeIndex = 0;
2271:
2272: renderOnceList.clear();
2273: timestampUpdateList.clear();
2274:
2275: defaultRenderMethod = null;
2276: text3DRenderMethod = null;
2277: vertexArrayRenderMethod = null;
2278: displayListRenderMethod = null;
2279: compressedGeometryRenderMethod = null;
2280: orientedShape3DRenderMethod = null;
2281: // Terminate MC thread
2282: running = false;
2283: }
2284: }
2285:
2286: /**
2287: * Note that we have to go through all views instead of
2288: * evaluate only the canvas in a single view since each screen
2289: * may share by multiple view
2290: */
2291: private void evaluateAllCanvases() {
2292:
2293: synchronized (renderThreadData) {
2294: // synchronized to prevent lost message when
2295: // renderThreadData is clear
2296:
2297: // First remove all renderrenderThreadData
2298: renderThreadData.clear();
2299:
2300: // Second reset canvasCount to zero
2301: View viewArr[] = (View[]) views.toArray(false);
2302: for (int i = views.size() - 1; i >= 0; i--) {
2303: viewArr[i].getCanvasList(true); // force canvas cache update
2304: Screen3D screens[] = viewArr[i].getScreens();
2305: for (int j = screens.length - 1; j >= 0; j--) {
2306: screens[j].canvasCount = 0;
2307: }
2308: }
2309:
2310: // Third create render thread and message thread
2311: for (int i = views.size() - 1; i >= 0; i--) {
2312: View v = viewArr[i];
2313: Canvas3D canvasList[][] = v.getCanvasList(false);
2314: if (!v.active) {
2315: continue;
2316: }
2317:
2318: for (int j = canvasList.length - 1; j >= 0; j--) {
2319: boolean added = false;
2320:
2321: for (int k = canvasList[j].length - 1; k >= 0; k--) {
2322: Canvas3D cv = canvasList[j][k];
2323:
2324: final Screen3D screen = cv.screen;
2325:
2326: if (cv.active) {
2327: if (screen.canvasCount++ == 0) {
2328: // Create Renderer, one per screen
2329: if (screen.renderer == null) {
2330: // get the renderer created for the graphics
2331: // device of the screen of the canvas
2332: // No need to synchronized since only
2333: // MC use it.
2334: Renderer rdr = (Renderer) screen.deviceRendererMap
2335: .get(cv.screen.graphicsDevice);
2336: if (rdr == null) {
2337: java.security.AccessController
2338: .doPrivileged(new java.security.PrivilegedAction() {
2339: public Object run() {
2340:
2341: synchronized (rootThreadGroup) {
2342: screen.renderer = new Renderer(
2343: rootThreadGroup);
2344: screen.renderer
2345: .setPriority(threadPriority);
2346: }
2347: return null;
2348: }
2349: });
2350: screen.renderer.initialize();
2351: screen.deviceRendererMap.put(
2352: screen.graphicsDevice,
2353: screen.renderer);
2354: } else {
2355: screen.renderer = rdr;
2356: }
2357: }
2358: }
2359: // offScreen canvases will be handled by the
2360: // request renderer, so don't add offScreen canvas
2361: // the render list
2362: //
2363: // Issue 131: Automatic offscreen canvases need to
2364: // be added to onscreen list. Special case.
2365: //
2366: // TODO KCR Issue 131: this should probably be
2367: // changed to a list of screens since multiple
2368: // off-screen canvases (either auto or manual) can
2369: // be used by the same renderer
2370: if (!cv.manualRendering) {
2371: screen.renderer.onScreen = screen;
2372: } else {
2373: screen.renderer.offScreen = screen;
2374: continue;
2375: }
2376:
2377: if (!added) {
2378: // Swap message data thread, one per
2379: // screen only. Note that we don't set
2380: // lastUpdateTime for this thread so
2381: // that it won't run in the first round
2382: J3dThreadData renderData = screen.renderer
2383: .getThreadData(v, null);
2384: renderThreadData.add(renderData);
2385:
2386: // only if renderBin is ready then we
2387: // update the lastUpdateTime to make it run
2388: if (v.renderBinReady) {
2389: renderData.lastUpdateTime = Math
2390: .max(
2391: currentTime,
2392: renderData.lastUpdateTime);
2393: }
2394: added = true;
2395: }
2396: // Renderer message data thread
2397: J3dThreadData renderData = screen.renderer
2398: .getThreadData(v, cv);
2399: renderThreadData.add(renderData);
2400: if (v.renderBinReady) {
2401: renderData.lastUpdateTime = Math.max(
2402: currentTime,
2403: renderData.lastUpdateTime);
2404: }
2405: }
2406: }
2407: }
2408:
2409: }
2410: }
2411:
2412: threadListsChanged = true;
2413: }
2414:
2415: private void evaluatePhysicalEnv(View v) {
2416: final PhysicalEnvironment env = v.physicalEnvironment;
2417:
2418: if (env.inputsched == null) {
2419: java.security.AccessController
2420: .doPrivileged(new java.security.PrivilegedAction() {
2421: public Object run() {
2422: synchronized (rootThreadGroup) {
2423: env.inputsched = new InputDeviceScheduler(
2424: rootThreadGroup, env);
2425: env.inputsched
2426: .setPriority(threadPriority);
2427: }
2428: return null;
2429: }
2430: });
2431: env.inputsched.start();
2432: PhysicalEnvironment.physicalEnvMap.put(env, env.inputsched);
2433: }
2434: threadListsChanged = true;
2435: }
2436:
2437: final private void addToStateThreads(J3dThreadData threadData) {
2438: if (threadData.thread.active) {
2439: stateWorkThreads.add(threadData);
2440: }
2441: }
2442:
2443: private void assignNewPrimaryView(VirtualUniverse univ) {
2444:
2445: View currentPrimary = univ.getCurrentView();
2446:
2447: if (currentPrimary != null) {
2448: currentPrimary.primaryView = false;
2449: }
2450:
2451: View v[] = (View[]) views.toArray(false);
2452: int nviews = views.size();
2453: for (int i = 0; i < nviews; i++) {
2454: View view = v[i];
2455: if (view.active && view.isRunning
2456: && (univ == view.universe)) {
2457: view.primaryView = true;
2458: univ.setCurrentView(view);
2459: return;
2460: }
2461: }
2462: univ.setCurrentView(null);
2463: }
2464:
2465: /**
2466: * This returns the default RenderMethod
2467: */
2468: RenderMethod getDefaultRenderMethod() {
2469: if (defaultRenderMethod == null) {
2470: defaultRenderMethod = new DefaultRenderMethod();
2471: }
2472: return defaultRenderMethod;
2473: }
2474:
2475: /**
2476: * This returns the text3d RenderMethod
2477: */
2478: RenderMethod getText3DRenderMethod() {
2479: if (text3DRenderMethod == null) {
2480: text3DRenderMethod = new Text3DRenderMethod();
2481: }
2482: return text3DRenderMethod;
2483: }
2484:
2485: /**
2486: * This returns the vertexArray RenderMethod
2487: */
2488: RenderMethod getVertexArrayRenderMethod() {
2489: if (vertexArrayRenderMethod == null) {
2490: vertexArrayRenderMethod = new VertexArrayRenderMethod();
2491: }
2492: return vertexArrayRenderMethod;
2493: }
2494:
2495: /**
2496: * This returns the displayList RenderMethod
2497: */
2498: RenderMethod getDisplayListRenderMethod() {
2499: if (displayListRenderMethod == null) {
2500: displayListRenderMethod = new DisplayListRenderMethod();
2501: }
2502: return displayListRenderMethod;
2503: }
2504:
2505: /**
2506: * This returns the compressed geometry RenderMethod
2507: */
2508: RenderMethod getCompressedGeometryRenderMethod() {
2509: if (compressedGeometryRenderMethod == null) {
2510: compressedGeometryRenderMethod = new CompressedGeometryRenderMethod();
2511: }
2512: return compressedGeometryRenderMethod;
2513: }
2514:
2515: /**
2516: * This returns the oriented shape3d RenderMethod
2517: */
2518: RenderMethod getOrientedShape3DRenderMethod() {
2519: if (orientedShape3DRenderMethod == null) {
2520: orientedShape3DRenderMethod = new OrientedShape3DRenderMethod();
2521: }
2522: return orientedShape3DRenderMethod;
2523: }
2524:
2525: /**
2526: * This notifies MasterControl that the given view has been activated
2527: */
2528: private void viewActivate(View v) {
2529:
2530: VirtualUniverse univ = v.universe;
2531:
2532: if (univ == null) {
2533: return;
2534: }
2535:
2536: if (!views.contains(v) || !regUniverseList.contains(univ)) {
2537: registerView(v);
2538: } else if (v.active) {
2539: evaluateAllCanvases();
2540: return;
2541: }
2542:
2543: if ((univ.activeViewCount == 0)) {
2544: univ.geometryStructure.resetConditionMet();
2545: univ.behaviorStructure.resetConditionMet();
2546: }
2547:
2548: if (v.isRunning) {
2549: numActiveViews++;
2550: univ.activeViewCount++;
2551: renderingAttributesStructure.updateThread.active = true;
2552: univ.transformStructure.updateThread.active = true;
2553: univ.geometryStructure.updateThread.active = true;
2554: univ.soundStructure.updateThread.active = true;
2555: univ.renderingEnvironmentStructure.updateThread.active = true;
2556: }
2557: univ.behaviorScheduler.active = true;
2558: univ.behaviorStructure.updateThread.active = true;
2559:
2560: activeUniverseList.addUnique(univ);
2561:
2562: if (v.isRunning) {
2563: v.soundScheduler.activate();
2564: v.renderBin.updateThread.active = true;
2565: }
2566: v.active = true;
2567:
2568: if (v.physicalEnvironment.activeViewRef++ == 0) {
2569: v.physicalEnvironment.inputsched.activate();
2570: }
2571:
2572: if (univ.getCurrentView() == null) {
2573: assignNewPrimaryView(univ);
2574: }
2575:
2576: evaluateAllCanvases();
2577: v.inRenderThreadData = true;
2578: threadListsChanged = true;
2579: // Notify GeometryStructure to query visible atom again
2580: // We should send message instead of just setting
2581: // v.vDirtyMask = View.VISIBILITY_POLICY_DIRTY;
2582: // since RenderBin may not run immediately next time.
2583: // In this case the dirty flag will lost since
2584: // updateViewCache() will reset it to 0.
2585: v.renderBin.reactivateView = true;
2586: }
2587:
2588: /**
2589: * Release context associate with view
2590: */
2591: private void freeContext(View v) {
2592: Canvas3D[][] canvasList = v.getCanvasList(false);
2593:
2594: for (int j = canvasList.length - 1; j >= 0; j--) {
2595: for (int k = canvasList[j].length - 1; k >= 0; k--) {
2596: Canvas3D cv = canvasList[j][k];
2597: if (!cv.validCanvas) {
2598: if ((cv.screen != null)
2599: && (cv.screen.renderer != null)) {
2600: rendererCleanupArgs[1] = cv;
2601: rendererCleanupArgs[2] = FREECONTEXT_CLEANUP;
2602: runMonitor(RUN_RENDERER_CLEANUP, null, null,
2603: null, cv.screen.renderer);
2604: rendererCleanupArgs[1] = null;
2605: }
2606: }
2607: }
2608: }
2609: }
2610:
2611: /**
2612: * This notifies MasterControl that the given view has been deactivated
2613: */
2614: private void viewDeactivate(View v) {
2615:
2616: if (!views.contains(v) || !v.active) {
2617: v.active = false;
2618: evaluateAllCanvases();
2619: return;
2620: }
2621:
2622: VirtualUniverse univ = v.universe;
2623:
2624: if (v.isRunning) {
2625: // if stopView() invoke before, no need to decrement count
2626: --numActiveViews;
2627: --univ.activeViewCount;
2628: }
2629:
2630: if (numActiveViews == 0) {
2631: renderingAttributesStructure.updateThread.active = false;
2632: }
2633:
2634: if (univ.activeViewCount == 0) {
2635: // check if destroyUniverseThread invoked before
2636: if (univ.behaviorScheduler != null) {
2637: univ.behaviorScheduler.deactivate();
2638: univ.transformStructure.updateThread.active = false;
2639: univ.geometryStructure.updateThread.active = false;
2640: univ.behaviorStructure.updateThread.active = false;
2641: univ.soundStructure.updateThread.active = false;
2642: univ.renderingEnvironmentStructure.updateThread.active = false;
2643: activeUniverseList.remove(univ);
2644: }
2645: }
2646:
2647: v.soundScheduler.deactivate();
2648: v.renderBin.updateThread.active = false;
2649: v.active = false;
2650: if (--v.physicalEnvironment.activeViewRef == 0) {
2651: v.physicalEnvironment.inputsched.deactivate();
2652: }
2653: assignNewPrimaryView(univ);
2654:
2655: evaluateAllCanvases();
2656:
2657: freeContext(v);
2658:
2659: v.inRenderThreadData = false;
2660: threadListsChanged = true;
2661: }
2662:
2663: /**
2664: * This notifies MasterControl to start given view
2665: */
2666: private void startView(View v) {
2667:
2668: if (!views.contains(v) || v.isRunning || !v.active) {
2669: v.isRunning = true;
2670: return;
2671: }
2672:
2673: numActiveViews++;
2674: renderingAttributesStructure.updateThread.active = true;
2675:
2676: VirtualUniverse univ = v.universe;
2677:
2678: univ.activeViewCount++;
2679: univ.transformStructure.updateThread.active = true;
2680: univ.geometryStructure.updateThread.active = true;
2681: univ.soundStructure.updateThread.active = true;
2682: univ.renderingEnvironmentStructure.updateThread.active = true;
2683: v.renderBin.updateThread.active = true;
2684: v.soundScheduler.activate();
2685: v.isRunning = true;
2686: if (univ.getCurrentView() == null) {
2687: assignNewPrimaryView(univ);
2688: }
2689: threadListsChanged = true;
2690: }
2691:
2692: /**
2693: * This notifies MasterControl to stop given view
2694: */
2695: private void stopView(View v) {
2696: if (!views.contains(v) || !v.isRunning || !v.active) {
2697: v.isRunning = false;
2698: return;
2699: }
2700:
2701: if (--numActiveViews == 0) {
2702: renderingAttributesStructure.updateThread.active = false;
2703: }
2704: VirtualUniverse univ = v.universe;
2705:
2706: if (--univ.activeViewCount == 0) {
2707: univ.transformStructure.updateThread.active = false;
2708: univ.geometryStructure.updateThread.active = false;
2709: univ.renderingEnvironmentStructure.updateThread.active = false;
2710: univ.soundStructure.updateThread.active = false;
2711: }
2712:
2713: v.renderBin.updateThread.active = false;
2714: v.soundScheduler.deactivate();
2715: v.isRunning = false;
2716: assignNewPrimaryView(univ);
2717: threadListsChanged = true;
2718: }
2719:
2720: // Call from user thread
2721: void addInputDeviceScheduler(InputDeviceScheduler ds) {
2722: synchronized (inputDeviceThreads) {
2723: inputDeviceThreads.add(ds);
2724: if (inputDeviceThreads.size() == 1) {
2725: timerThread.addInputDeviceSchedCond();
2726: }
2727: }
2728: postRequest(INPUTDEVICE_CHANGE, null);
2729: }
2730:
2731: // Call from user thread
2732: void removeInputDeviceScheduler(InputDeviceScheduler ds) {
2733: inputDeviceThreads.remove(ds);
2734: postRequest(INPUTDEVICE_CHANGE, null);
2735: }
2736:
2737: /**
2738: * Add an object to the mirror object list
2739: */
2740: void addMirrorObject(ObjectUpdate o) {
2741: mirrorObjects.add(o);
2742: }
2743:
2744: /**
2745: * This updates any mirror objects. It is called when threads
2746: * are done.
2747: */
2748: void updateMirrorObjects() {
2749: ObjectUpdate objs[] = (ObjectUpdate[]) mirrorObjects
2750: .toArray(false);
2751: int sz = mirrorObjects.arraySize();
2752:
2753: for (int i = 0; i < sz; i++) {
2754: objs[i].updateObject();
2755: }
2756: mirrorObjects.clear();
2757: }
2758:
2759: /**
2760: * This fun little method does all the hard work of setting up the
2761: * work thread list.
2762: */
2763: private void updateWorkThreads() {
2764:
2765: stateWorkThreads.clear();
2766: renderWorkThreads.clear();
2767: requestRenderWorkThreads.clear();
2768:
2769: // First the global rendering attributes structure update
2770: if (numActiveViews > 0) {
2771: addToStateThreads(renderingAttributesStructure
2772: .getUpdateThreadData());
2773: }
2774:
2775: // Next, each of the transform structure updates
2776: VirtualUniverse universes[] = (VirtualUniverse[]) activeUniverseList
2777: .toArray(false);
2778: VirtualUniverse univ;
2779: int i;
2780: int size = activeUniverseList.arraySize();
2781:
2782: for (i = size - 1; i >= 0; i--) {
2783: addToStateThreads(universes[i].transformStructure
2784: .getUpdateThreadData());
2785: }
2786: lastTransformStructureThread = stateWorkThreads.size();
2787:
2788: // Next, the GeometryStructure, BehaviorStructure,
2789: // RenderingEnvironmentStructure, and SoundStructure
2790: for (i = size - 1; i >= 0; i--) {
2791: univ = universes[i];
2792: addToStateThreads(univ.geometryStructure
2793: .getUpdateThreadData());
2794: addToStateThreads(univ.behaviorStructure
2795: .getUpdateThreadData());
2796: addToStateThreads(univ.renderingEnvironmentStructure
2797: .getUpdateThreadData());
2798: addToStateThreads(univ.soundStructure.getUpdateThreadData());
2799: }
2800:
2801: lastStructureUpdateThread = stateWorkThreads.size();
2802:
2803: // Next, the BehaviorSchedulers
2804: for (i = size - 1; i >= 0; i--) {
2805: addToStateThreads(universes[i].behaviorScheduler
2806: .getThreadData(null, null));
2807: }
2808:
2809: // Now InputDeviceScheduler
2810:
2811: InputDeviceScheduler ds[] = (InputDeviceScheduler[]) inputDeviceThreads
2812: .toArray(true);
2813: for (i = inputDeviceThreads.size() - 1; i >= 0; i--) {
2814: J3dThreadData threadData = ds[i].getThreadData();
2815: threadData.thread.active = true;
2816: addToStateThreads(threadData);
2817: }
2818:
2819: // Now the RenderBins and SoundSchedulers
2820: View viewArr[] = (View[]) views.toArray(false);
2821: J3dThreadData thread;
2822:
2823: for (i = views.size() - 1; i >= 0; i--) {
2824: View v = viewArr[i];
2825: if (v.active && v.isRunning) {
2826: addToStateThreads(v.renderBin.getUpdateThreadData());
2827: addToStateThreads(v.soundScheduler
2828: .getUpdateThreadData());
2829: Canvas3D canvasList[][] = v.getCanvasList(false);
2830: int longestScreenList = v.getLongestScreenList();
2831: Object args[] = null;
2832: // renderer render
2833: for (int j = 0; j < longestScreenList; j++) {
2834: for (int k = 0; k < canvasList.length; k++) {
2835: if (j < canvasList[k].length) {
2836: Canvas3D cv = canvasList[k][j];
2837: // Issue 131: setup renderer unless manualRendering
2838: if (cv.active && cv.isRunningStatus
2839: && !cv.manualRendering) {
2840: if (cv.screen.renderer == null) {
2841: continue;
2842: }
2843: thread = cv.screen.renderer
2844: .getThreadData(v, cv);
2845: renderWorkThreads.add(thread);
2846: args = (Object[]) thread.threadArgs;
2847: args[0] = RENDER;
2848: args[1] = cv;
2849: args[2] = v;
2850: }
2851: }
2852: }
2853: }
2854:
2855: // renderer swap
2856: for (int j = 0; j < canvasList.length; j++) {
2857: for (int k = 0; k < canvasList[j].length; k++) {
2858: Canvas3D cv = canvasList[j][k];
2859: // create swap thread only if there is at
2860: // least one active canvas
2861: // Issue 131: only if not manualRendering
2862: if (cv.active && cv.isRunningStatus
2863: && !cv.manualRendering) {
2864: if (cv.screen.renderer == null) {
2865: // Should not happen
2866: continue;
2867: }
2868: thread = cv.screen.renderer.getThreadData(
2869: v, null);
2870: renderWorkThreads.add(thread);
2871: args = (Object[]) thread.threadArgs;
2872: args[0] = SWAP;
2873: args[1] = v;
2874: args[2] = canvasList[j];
2875: break;
2876: }
2877: }
2878: }
2879: }
2880: }
2881:
2882: thread = null;
2883:
2884: for (Enumeration e = Screen3D.deviceRendererMap.elements(); e
2885: .hasMoreElements();) {
2886: Renderer rdr = (Renderer) e.nextElement();
2887: thread = rdr.getThreadData(null, null);
2888: requestRenderWorkThreads.add(thread);
2889: thread.threadOpts = J3dThreadData.CONT_THREAD;
2890: ((Object[]) thread.threadArgs)[0] = REQUESTRENDER;
2891: }
2892:
2893: if (thread != null) {
2894: thread.threadOpts |= J3dThreadData.WAIT_ALL_THREADS;
2895: }
2896:
2897: threadListsChanged = false;
2898:
2899: // dumpWorkThreads();
2900: }
2901:
2902: void dumpWorkThreads() {
2903: System.err.println("-----------------------------");
2904: System.err.println("MasterControl/dumpWorkThreads");
2905:
2906: J3dThreadData threads[];
2907: int size = 0;
2908:
2909: for (int k = 0; k < 3; k++) {
2910: switch (k) {
2911: case 0:
2912: threads = (J3dThreadData[]) stateWorkThreads
2913: .toArray(false);
2914: size = stateWorkThreads.arraySize();
2915: break;
2916: case 1:
2917: threads = (J3dThreadData[]) renderWorkThreads
2918: .toArray(false);
2919: size = renderWorkThreads.arraySize();
2920: break;
2921: default:
2922: threads = (J3dThreadData[]) requestRenderWorkThreads
2923: .toArray(false);
2924: size = requestRenderWorkThreads.arraySize();
2925: break;
2926: }
2927:
2928: for (int i = 0; i < size; i++) {
2929: J3dThreadData thread = threads[i];
2930: System.err
2931: .println("Thread " + i + ": " + thread.thread);
2932: System.err.println("\tOps: " + thread.threadOpts);
2933: if (thread.threadArgs != null) {
2934: Object[] args = (Object[]) thread.threadArgs;
2935: System.err.print("\tArgs: ");
2936: for (int j = 0; j < args.length; j++) {
2937: System.err.print(args[j] + " ");
2938: }
2939: }
2940: System.err.println("");
2941: }
2942: }
2943: System.err.println("-----------------------------");
2944: }
2945:
2946: /**
2947: * A convienence wrapper function for various parts of the system
2948: * to force MC to run.
2949: */
2950: final void setWork() {
2951: runMonitor(SET_WORK, null, null, null, null);
2952: }
2953:
2954: final void setWorkForRequestRenderer() {
2955: runMonitor(SET_WORK_FOR_REQUEST_RENDERER, null, null, null,
2956: null);
2957: }
2958:
2959: /**
2960: * Call from GraphicsConfigTemplate to evaluate current
2961: * capabilities using Renderer thread to invoke native
2962: * graphics library functions. This avoid MT-safe problem
2963: * when using thread directly invoke graphics functions.
2964: */
2965: void sendRenderMessage(GraphicsConfiguration gc, Object arg,
2966: Integer mtype) {
2967: Renderer rdr = createRenderer(gc);
2968: J3dMessage renderMessage = new J3dMessage();
2969: renderMessage.threads = J3dThread.RENDER_THREAD;
2970: renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
2971: renderMessage.universe = null;
2972: renderMessage.view = null;
2973: renderMessage.args[0] = null;
2974: renderMessage.args[1] = arg;
2975: renderMessage.args[2] = mtype;
2976: rdr.rendererStructure.addMessage(renderMessage);
2977: setWorkForRequestRenderer();
2978: }
2979:
2980: // Issue for Issue 175
2981: // Pass DestroyCtxAndOffScreenBuffer to the Renderer thread for execution.
2982: void sendDestroyCtxAndOffScreenBuffer(Canvas3D c) {
2983: // Assertion check. Look for comment in sendCreateOffScreenBuffer.
2984: GraphicsDevice gd = c.graphicsConfiguration.getDevice();
2985: assert Screen3D.deviceRendererMap.get(gd) != null;
2986:
2987: synchronized (mcThreadLock) {
2988: // Issue 364: create master control thread if needed
2989: createMasterControlThread();
2990: assert mcThread != null;
2991:
2992: Renderer rdr = createRenderer(c.graphicsConfiguration);
2993: J3dMessage createMessage = new J3dMessage();
2994: createMessage.threads = J3dThread.RENDER_THREAD;
2995: createMessage.type = J3dMessage.DESTROY_CTX_AND_OFFSCREENBUFFER;
2996: createMessage.universe = null;
2997: createMessage.view = null;
2998: createMessage.args[0] = c;
2999: // Fix for issue 340: send display, drawable & ctx in msg
3000: createMessage.args[1] = new Long(c.screen.display);
3001: createMessage.args[2] = c.drawable;
3002: createMessage.args[3] = c.ctx;
3003: rdr.rendererStructure.addMessage(createMessage);
3004: synchronized (requestObjList) {
3005: setWorkForRequestRenderer();
3006: pendingRequest = true;
3007: }
3008: }
3009: }
3010:
3011: // Fix for Issue 18
3012: // Pass CreateOffScreenBuffer to the Renderer thread for execution.
3013: void sendCreateOffScreenBuffer(Canvas3D c) {
3014: // Assertion check that the renderer has already been created.
3015: // If it hasn't, this is very, very bad because it opens up
3016: // the possibility of an MT race condition since this method
3017: // can be called from the user's thread, possibly at the same
3018: // time as the MasterControl thread is trying to create a new
3019: // Renderer. Fortunately, this should never happen since both
3020: // the GraphicsTemplate3D methods that return a valid Graphics
3021: // Configuration and the Canvas3D constructor will ultimately
3022: // cause a renderer to be created via sendRenderMessage().
3023: GraphicsDevice gd = c.graphicsConfiguration.getDevice();
3024: J3dDebug.doAssert((Screen3D.deviceRendererMap.get(gd) != null),
3025: "Screen3D.deviceRendererMap.get(gd) != null");
3026:
3027: synchronized (mcThreadLock) {
3028: // Create master control thread if needed
3029: createMasterControlThread();
3030: assert mcThread != null;
3031:
3032: // Fix for Issue 72 : call createRenderer rather than getting
3033: // the renderer from the canvas.screen object
3034: Renderer rdr = createRenderer(c.graphicsConfiguration);
3035: J3dMessage createMessage = new J3dMessage();
3036: createMessage.threads = J3dThread.RENDER_THREAD;
3037: createMessage.type = J3dMessage.CREATE_OFFSCREENBUFFER;
3038: createMessage.universe = null;
3039: createMessage.view = null;
3040: createMessage.args[0] = c;
3041: rdr.rendererStructure.addMessage(createMessage);
3042: synchronized (requestObjList) {
3043: setWorkForRequestRenderer();
3044: pendingRequest = true;
3045: }
3046: }
3047: }
3048:
3049: // Issue 347 - Pass AllocateCanvasId to the Renderer thread for execution
3050: void sendAllocateCanvasId(Canvas3D c) {
3051: synchronized (mcThreadLock) {
3052: // Issue 364: create master control thread if needed
3053: createMasterControlThread();
3054: assert mcThread != null;
3055:
3056: Renderer rdr = createRenderer(c.graphicsConfiguration);
3057: J3dMessage createMessage = new J3dMessage();
3058: createMessage.threads = J3dThread.RENDER_THREAD;
3059: createMessage.type = J3dMessage.ALLOCATE_CANVASID;
3060: createMessage.universe = null;
3061: createMessage.view = null;
3062: createMessage.args[0] = c;
3063: rdr.rendererStructure.addMessage(createMessage);
3064: synchronized (requestObjList) {
3065: setWorkForRequestRenderer();
3066: pendingRequest = true;
3067: }
3068: }
3069: }
3070:
3071: // Issue 347 - Pass AllocateCanvasId to the Renderer thread for execution
3072: void sendFreeCanvasId(Canvas3D c) {
3073: synchronized (mcThreadLock) {
3074: // Issue 364: create master control thread if needed
3075: createMasterControlThread();
3076: assert mcThread != null;
3077:
3078: Renderer rdr = createRenderer(c.graphicsConfiguration);
3079: J3dMessage createMessage = new J3dMessage();
3080: createMessage.threads = J3dThread.RENDER_THREAD;
3081: createMessage.type = J3dMessage.FREE_CANVASID;
3082: createMessage.universe = null;
3083: createMessage.view = null;
3084: createMessage.args[0] = c;
3085: rdr.rendererStructure.addMessage(createMessage);
3086: synchronized (requestObjList) {
3087: setWorkForRequestRenderer();
3088: pendingRequest = true;
3089: }
3090: }
3091: }
3092:
3093: /**
3094: * This is the MasterControl work method for Java 3D
3095: */
3096: void doWork() {
3097: runMonitor(CHECK_FOR_WORK, null, null, null, null);
3098:
3099: synchronized (timeLock) {
3100: synchronized (requestObjList) {
3101: if (pendingRequest) {
3102: handlePendingRequest();
3103: }
3104: }
3105: }
3106:
3107: if (!running) {
3108: return;
3109: }
3110:
3111: if (threadListsChanged) { // Check for new Threads
3112: updateWorkThreads();
3113: }
3114:
3115: synchronized (timeLock) {
3116: // This is neccesary to prevent updating
3117: // thread.lastUpdateTime from user thread
3118: // in sendMessage() or sendRunMessage()
3119: updateTimeValues();
3120: }
3121:
3122: //This is temporary until the view model is updated
3123: View v[] = (View[]) views.toArray(false);
3124: for (int i = views.size() - 1; i >= 0; i--) {
3125: if (v[i].active) {
3126: v[i].updateViewCache();
3127: // update OrientedShape3D
3128: if ((v[i].viewCache.vcDirtyMask != 0 && !v[i].renderBin.orientedRAs
3129: .isEmpty())
3130: || (v[i].renderBin.cachedDirtyOrientedRAs != null && !v[i].renderBin.cachedDirtyOrientedRAs
3131: .isEmpty())) {
3132: v[i].renderBin.updateOrientedRAs();
3133: }
3134: }
3135: }
3136:
3137: runMonitor(RUN_THREADS, stateWorkThreads, renderWorkThreads,
3138: requestRenderWorkThreads, null);
3139:
3140: if (renderOnceList.size() > 0) {
3141: clearRenderOnceList();
3142: }
3143:
3144: manageMemory();
3145:
3146: }
3147:
3148: private void handlePendingRequest() {
3149:
3150: Object objs[];
3151: Integer types[];
3152: int size;
3153: boolean rendererRun = false;
3154:
3155: objs = requestObjList.toArray(false);
3156: types = (Integer[]) requestTypeList.toArray(false);
3157: size = requestObjList.size();
3158:
3159: for (int i = 0; i < size; i++) {
3160: // need to process request in order
3161: Integer type = types[i];
3162: Object o = objs[i];
3163: if (type == RESET_CANVAS) {
3164: Canvas3D cv = (Canvas3D) o;
3165: if ((cv.screen != null) && (cv.screen.renderer != null)) {
3166: rendererCleanupArgs[1] = o;
3167: rendererCleanupArgs[2] = RESETCANVAS_CLEANUP;
3168: runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
3169: cv.screen.renderer);
3170: rendererCleanupArgs[1] = null;
3171: }
3172: cv.reset();
3173: cv.view = null;
3174: cv.computeViewCache();
3175: } else if (type == ACTIVATE_VIEW) {
3176: viewActivate((View) o);
3177: } else if (type == DEACTIVATE_VIEW) {
3178: viewDeactivate((View) o);
3179: } else if (type == REEVALUATE_CANVAS) {
3180: evaluateAllCanvases();
3181: } else if (type == INPUTDEVICE_CHANGE) {
3182: inputDeviceThreads.clearMirror();
3183: threadListsChanged = true;
3184: } else if (type == START_VIEW) {
3185: startView((View) o);
3186: } else if (type == STOP_VIEW) {
3187: View v = (View) o;
3188: // Collision takes 3 rounds to finish its request
3189: if (++v.stopViewCount > 4) {
3190: v.stopViewCount = -1; // reset counter
3191: stopView(v);
3192: } else {
3193: tempViewList.add(v);
3194: }
3195: } else if (type == UNREGISTER_VIEW) {
3196: unregisterView((View) o);
3197: } else if (type == PHYSICAL_ENV_CHANGE) {
3198: evaluatePhysicalEnv((View) o);
3199: } else if (type == EMPTY_UNIVERSE) {
3200: // Issue 81: We need to process this message as long
3201: // as there are no views associated with this
3202: // universe. Previously, this message was ignored if
3203: // there were views associated with *any* universe,
3204: // which led to a memory / thread leak.
3205: boolean foundView = false;
3206: VirtualUniverse univ = (VirtualUniverse) o;
3207: View v[] = (View[]) views.toArray(false);
3208: for (int j = views.size() - 1; j >= 0; j--) {
3209: if (v[j].universe == univ) {
3210: foundView = true;
3211: break;
3212: }
3213: }
3214: if (!foundView) {
3215: destroyUniverseThreads(univ);
3216: threadListsChanged = true;
3217: }
3218: } else if (type == START_RENDERER) {
3219: if (o instanceof Canvas3D) {
3220: Canvas3D c3d = (Canvas3D) o;
3221: if (!c3d.isFatalError()) {
3222: c3d.isRunningStatus = true;
3223: }
3224: } else {
3225: ((Renderer) o).userStop = false;
3226: }
3227: threadListsChanged = true;
3228: } else if (type == STOP_RENDERER) {
3229: if (o instanceof Canvas3D) {
3230: ((Canvas3D) o).isRunningStatus = false;
3231: } else {
3232: ((Renderer) o).userStop = true;
3233: }
3234: threadListsChanged = true;
3235: } else if (type == RENDER_ONCE) {
3236: View v = (View) o;
3237: // temporary start View for renderonce
3238: // it will stop afterwards
3239: startView(v);
3240: renderOnceList.add(v);
3241: sendRunMessage(v, J3dThread.UPDATE_RENDER);
3242: threadListsChanged = true;
3243: rendererRun = true;
3244: } else if (type == FREE_CONTEXT) {
3245: Canvas3D cv = (Canvas3D) ((Object[]) o)[0];
3246: if ((cv.screen != null) && (cv.screen.renderer != null)) {
3247: rendererCleanupArgs[1] = o;
3248: rendererCleanupArgs[2] = REMOVECTX_CLEANUP;
3249: runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
3250: cv.screen.renderer);
3251: rendererCleanupArgs[1] = null;
3252: }
3253: rendererRun = true;
3254: } else if (type == FREE_DRAWING_SURFACE) {
3255: Pipeline.getPipeline().freeDrawingSurfaceNative(o);
3256: } else if (type == GETBESTCONFIG) {
3257: GraphicsConfiguration gc = ((GraphicsConfiguration[]) ((GraphicsConfigTemplate3D) o).testCfg)[0];
3258: sendRenderMessage(gc, o, type);
3259: rendererRun = true;
3260: } else if (type == ISCONFIGSUPPORT) {
3261: GraphicsConfiguration gc = (GraphicsConfiguration) ((GraphicsConfigTemplate3D) o).testCfg;
3262: sendRenderMessage(gc, o, type);
3263: rendererRun = true;
3264: } else if ((type == SET_GRAPHICSCONFIG_FEATURES)
3265: || (type == SET_QUERYPROPERTIES)) {
3266: GraphicsConfiguration gc = (GraphicsConfiguration) ((Canvas3D) o).graphicsConfiguration;
3267: sendRenderMessage(gc, o, type);
3268: rendererRun = true;
3269: } else if (type == SET_VIEW) {
3270: Canvas3D cv = (Canvas3D) o;
3271: cv.view = cv.pendingView;
3272: cv.computeViewCache();
3273: }
3274: }
3275:
3276: // Do it only after all universe/View is register
3277: for (int i = 0; i < size; i++) {
3278: Integer type = types[i];
3279: if (type == FREE_MESSAGE) {
3280: if (objs[i] instanceof VirtualUniverse) {
3281: VirtualUniverse u = (VirtualUniverse) objs[i];
3282: if (!regUniverseList.contains(u)) {
3283: emptyMessageList(u.behaviorStructure, null);
3284: emptyMessageList(u.geometryStructure, null);
3285: emptyMessageList(u.soundStructure, null);
3286: emptyMessageList(
3287: u.renderingEnvironmentStructure, null);
3288: }
3289: } else if (objs[i] instanceof View) {
3290: View v = (View) objs[i];
3291: if (!views.contains(v)) {
3292: emptyMessageList(v.soundScheduler, v);
3293: emptyMessageList(v.renderBin, v);
3294: if (v.resetUnivCount == v.universeCount) {
3295: v.reset();
3296: v.universe = null;
3297: if (running == false) {
3298: // MC is about to terminate
3299:
3300: /*
3301: // Don't free list cause there may
3302: // have some other thread returning ID
3303: // after it.
3304: FreeListManager.clearList(FreeListManager.DISPLAYLIST);
3305: FreeListManager.clearList(FreeListManager.TEXTURE2D);
3306: FreeListManager.clearList(FreeListManager.TEXTURE3D);
3307:
3308: synchronized (textureIdLock) {
3309: textureIdCount = 0;
3310: }
3311: */
3312: }
3313: }
3314: }
3315: }
3316:
3317: }
3318:
3319: }
3320: requestObjList.clear();
3321: requestTypeList.clear();
3322:
3323: size = tempViewList.size();
3324: if (size > 0) {
3325: if (running) {
3326: for (int i = 0; i < size; i++) {
3327: requestTypeList.add(STOP_VIEW);
3328: requestObjList.add(tempViewList.get(i));
3329: }
3330: setWork();
3331: } else { // MC will shutdown
3332: for (int i = 0; i < size; i++) {
3333: View v = (View) tempViewList.get(i);
3334: v.stopViewCount = -1;
3335: v.isRunning = false;
3336: }
3337: }
3338: tempViewList.clear();
3339: pendingRequest = true;
3340: } else {
3341: pendingRequest = rendererRun || (requestObjList.size() > 0);
3342:
3343: }
3344:
3345: size = freeMessageList.size();
3346: if (size > 0) {
3347: for (int i = 0; i < size; i++) {
3348: requestTypeList.add(FREE_MESSAGE);
3349: requestObjList.add(freeMessageList.get(i));
3350: }
3351: pendingRequest = true;
3352: freeMessageList.clear();
3353: }
3354: if (!running && (renderOnceList.size() > 0)) {
3355: clearRenderOnceList();
3356: }
3357:
3358: if (pendingRequest) {
3359: setWork();
3360: }
3361:
3362: if (rendererRun || requestRenderWorkToDo) {
3363: running = true;
3364: }
3365:
3366: }
3367:
3368: private void clearRenderOnceList() {
3369: for (int i = renderOnceList.size() - 1; i >= 0; i--) {
3370: View v = (View) renderOnceList.get(i);
3371: v.renderOnceFinish = true;
3372: // stop after render once
3373: stopView(v);
3374: }
3375: renderOnceList.clear();
3376: threadListsChanged = true;
3377:
3378: }
3379:
3380: synchronized void runMonitor(int action,
3381: UnorderList stateThreadList, UnorderList renderThreadList,
3382: UnorderList requestRenderThreadList, J3dThread nthread) {
3383:
3384: switch (action) {
3385: case RUN_THREADS:
3386: int currentStateThread = 0;
3387: int currentRenderThread = 0;
3388: int currentRequestRenderThread = 0;
3389: View view;
3390: boolean done;
3391: J3dThreadData thread;
3392: J3dThreadData renderThreads[] = (J3dThreadData[]) renderThreadList
3393: .toArray(false);
3394: J3dThreadData stateThreads[] = (J3dThreadData[]) stateThreadList
3395: .toArray(false);
3396: J3dThreadData requestRenderThreads[] = (J3dThreadData[]) requestRenderThreadList
3397: .toArray(false);
3398: int renderThreadSize = renderThreadList.arraySize();
3399: int stateThreadSize = stateThreadList.arraySize();
3400: int requestRenderThreadSize = requestRenderThreadList
3401: .arraySize();
3402:
3403: done = false;
3404:
3405: //lock all the needed geometry and image component
3406: View[] allView = (View[]) views.toArray(false);
3407: View currentV;
3408: int i;
3409:
3410: if (lockGeometry) {
3411: for (i = views.arraySize() - 1; i >= 0; i--) {
3412: currentV = allView[i];
3413: currentV.renderBin.lockGeometry();
3414: }
3415: }
3416:
3417: while (!done) {
3418: // First try a RenderThread
3419: while (!renderWaiting
3420: && currentRenderThread != renderThreadSize) {
3421: thread = renderThreads[currentRenderThread++];
3422: if (!thread.needsRun) {
3423: continue;
3424: }
3425: if ((thread.threadOpts & J3dThreadData.START_TIMER) != 0) {
3426: view = (View) ((Object[]) thread.threadArgs)[2];
3427: view.frameNumber++;
3428: view.startTime = J3dClock.currentTimeMillis();
3429: }
3430:
3431: renderPending++;
3432:
3433: if (cpuLimit == 1) {
3434: thread.thread.args = (Object[]) thread.threadArgs;
3435: thread.thread.doWork(currentTime);
3436: } else {
3437: threadPending++;
3438: thread.thread.runMonitor(J3dThread.RUN,
3439: currentTime,
3440: (Object[]) thread.threadArgs);
3441: }
3442:
3443: if ((thread.threadOpts & J3dThreadData.STOP_TIMER) != 0) {
3444: view = (View) ((Object[]) thread.threadArgs)[3];
3445: timestampUpdateList.add(view);
3446: }
3447:
3448: if ((thread.threadOpts & J3dThreadData.LAST_STOP_TIMER) != 0) {
3449: // release lock on locked geometry and image component
3450: for (i = 0; i < views.arraySize(); i++) {
3451: currentV = allView[i];
3452: currentV.renderBin.releaseGeometry();
3453: }
3454: }
3455:
3456: if ((cpuLimit != 1)
3457: && (thread.threadOpts & J3dThreadData.WAIT_ALL_THREADS) != 0) {
3458:
3459: renderWaiting = true;
3460: }
3461:
3462: if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
3463: state = WAITING_FOR_CPU;
3464: try {
3465: wait();
3466: } catch (InterruptedException e) {
3467: System.err.println(e);
3468: }
3469: state = RUNNING;
3470: }
3471:
3472: }
3473: // Now try state threads
3474: while (!stateWaiting
3475: && currentStateThread != stateThreadSize) {
3476: thread = stateThreads[currentStateThread++];
3477:
3478: if (!thread.needsRun) {
3479: continue;
3480: }
3481:
3482: statePending++;
3483:
3484: if (cpuLimit == 1) {
3485: thread.thread.args = (Object[]) thread.threadArgs;
3486: thread.thread.doWork(currentTime);
3487: } else {
3488: threadPending++;
3489: thread.thread.runMonitor(J3dThread.RUN,
3490: currentTime,
3491: (Object[]) thread.threadArgs);
3492: }
3493: if (cpuLimit != 1
3494: && (thread.threadOpts & J3dThreadData.WAIT_ALL_THREADS) != 0) {
3495: stateWaiting = true;
3496: }
3497:
3498: if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
3499: // Fix bug 4686766 - always allow
3500: // renderer thread to continue if not finish
3501: // geomLock can release for Behavior thread to
3502: // continue.
3503: if (currentRenderThread == renderThreadSize) {
3504: state = WAITING_FOR_CPU;
3505: try {
3506: wait();
3507: } catch (InterruptedException e) {
3508: System.err.println(e);
3509: }
3510: state = RUNNING;
3511: } else {
3512: // Run renderer thread next time
3513: break;
3514: }
3515:
3516: }
3517: }
3518:
3519: // Now try requestRender threads
3520: if (!renderWaiting
3521: && (currentRenderThread == renderThreadSize)) {
3522: currentRequestRenderThread = 0;
3523: while (!renderWaiting
3524: && (currentRequestRenderThread != requestRenderThreadSize)) {
3525:
3526: thread = requestRenderThreads[currentRequestRenderThread++];
3527:
3528: renderPending++;
3529:
3530: if (cpuLimit == 1) {
3531: thread.thread.args = (Object[]) thread.threadArgs;
3532: thread.thread.doWork(currentTime);
3533: } else {
3534: threadPending++;
3535: thread.thread.runMonitor(J3dThread.RUN,
3536: currentTime,
3537: (Object[]) thread.threadArgs);
3538: }
3539: if (cpuLimit != 1
3540: && (thread.threadOpts & J3dThreadData.WAIT_ALL_THREADS) != 0) {
3541: renderWaiting = true;
3542: }
3543: if (cpuLimit != 1 && cpuLimit <= threadPending) {
3544: state = WAITING_FOR_CPU;
3545: try {
3546: wait();
3547: } catch (InterruptedException e) {
3548: System.err.println(e);
3549: }
3550: state = RUNNING;
3551: }
3552: }
3553: }
3554:
3555: if (cpuLimit != 1) {
3556: if ((renderWaiting && (currentStateThread == stateThreadSize))
3557: || (stateWaiting && currentRenderThread == renderThreadSize)
3558: || (renderWaiting && stateWaiting)) {
3559: if (!requestRenderWorkToDo) {
3560: state = WAITING_FOR_THREADS;
3561: try {
3562: wait();
3563: } catch (InterruptedException e) {
3564: System.err.println(e);
3565: }
3566: state = RUNNING;
3567: }
3568: requestRenderWorkToDo = false;
3569: }
3570: }
3571:
3572: if ((currentStateThread == stateThreadSize)
3573: && (currentRenderThread == renderThreadSize)
3574: && (currentRequestRenderThread == requestRenderThreadSize)
3575: && (threadPending == 0)) {
3576: for (int k = timestampUpdateList.size() - 1; k >= 0; k--) {
3577: View v = (View) timestampUpdateList.get(k);
3578: v.setFrameTimingValues();
3579: v.universe.behaviorStructure.incElapsedFrames();
3580: }
3581: timestampUpdateList.clear();
3582: updateMirrorObjects();
3583: done = true;
3584:
3585: if (isStatsLoggable(Level.INFO)) {
3586: // Instrumentation of Java 3D renderer
3587: logTimes();
3588: }
3589: }
3590: }
3591: break;
3592:
3593: case THREAD_DONE:
3594: if (state != WAITING_FOR_RENDERER_CLEANUP) {
3595:
3596: threadPending--;
3597: assert threadPending >= 0 : ("threadPending = " + threadPending);
3598: if (nthread.type == J3dThread.RENDER_THREAD) {
3599: View v = (View) nthread.args[3];
3600: if (v != null) { // STOP_TIMER
3601: v.stopTime = J3dClock.currentTimeMillis();
3602: }
3603:
3604: if (--renderPending == 0) {
3605: renderWaiting = false;
3606: }
3607: assert renderPending >= 0 : ("renderPending = " + renderPending);
3608: } else {
3609: if (--statePending == 0) {
3610: stateWaiting = false;
3611: }
3612: assert statePending >= 0 : ("statePending = " + statePending);
3613: }
3614: if (state == WAITING_FOR_CPU
3615: || state == WAITING_FOR_THREADS) {
3616: notify();
3617: }
3618: } else {
3619: notify();
3620: state = RUNNING;
3621: }
3622: break;
3623:
3624: case CHECK_FOR_WORK:
3625: if (!workToDo) {
3626: state = SLEEPING;
3627: // NOTE: this could wakeup spuriously (see issue 279), but it
3628: // will not cause any problems.
3629: try {
3630: wait();
3631: } catch (InterruptedException e) {
3632: System.err.println(e);
3633: }
3634: state = RUNNING;
3635: }
3636: workToDo = false;
3637: break;
3638:
3639: case SET_WORK:
3640: workToDo = true;
3641: if (state == SLEEPING) {
3642: notify();
3643: }
3644: break;
3645:
3646: case SET_WORK_FOR_REQUEST_RENDERER:
3647: requestRenderWorkToDo = true;
3648: workToDo = true;
3649: if (state == WAITING_FOR_CPU
3650: || state == WAITING_FOR_THREADS
3651: || state == SLEEPING) {
3652: notify();
3653: }
3654: break;
3655:
3656: case RUN_RENDERER_CLEANUP:
3657: nthread.runMonitor(J3dThread.RUN, currentTime,
3658: rendererCleanupArgs);
3659: state = WAITING_FOR_RENDERER_CLEANUP;
3660: // Issue 279 - loop until state is set to running
3661: while (state != RUNNING) {
3662: try {
3663: wait();
3664: } catch (InterruptedException e) {
3665: System.err.println(e);
3666: }
3667: }
3668: break;
3669:
3670: default:
3671: // Should never get here
3672: assert false : "missing case in switch statement";
3673: }
3674: }
3675:
3676: // Static initializer
3677: static {
3678: // create ThreadGroup
3679: java.security.AccessController
3680: .doPrivileged(new java.security.PrivilegedAction() {
3681: public Object run() {
3682: ThreadGroup parent;
3683: Thread thread = Thread.currentThread();
3684: threadPriority = thread.getPriority();
3685: rootThreadGroup = thread.getThreadGroup();
3686: while ((parent = rootThreadGroup.getParent()) != null) {
3687: rootThreadGroup = parent;
3688: }
3689: rootThreadGroup = new ThreadGroup(
3690: rootThreadGroup, "Java3D");
3691: // use the default maximum group priority
3692: return null;
3693: }
3694: });
3695:
3696: // Initialize loggers
3697: try {
3698: initLoggers();
3699: } catch (RuntimeException ex) {
3700: System.err.println(ex);
3701: }
3702: }
3703:
3704: static String mtype[] = { "INSERT_NODES", "REMOVE_NODES", "RUN",
3705: "TRANSFORM_CHANGED", "UPDATE_VIEW", "STOP_THREAD",
3706: "COLORINGATTRIBUTES_CHANGED", "LINEATTRIBUTES_CHANGED",
3707: "POINTATTRIBUTES_CHANGED", "POLYGONATTRIBUTES_CHANGED",
3708: "RENDERINGATTRIBUTES_CHANGED", "TEXTUREATTRIBUTES_CHANGED",
3709: "TRANSPARENCYATTRIBUTES_CHANGED", "MATERIAL_CHANGED",
3710: "TEXCOORDGENERATION_CHANGED", "TEXTURE_CHANGED",
3711: "MORPH_CHANGED", "GEOMETRY_CHANGED", "APPEARANCE_CHANGED",
3712: "LIGHT_CHANGED", "BACKGROUND_CHANGED", "CLIP_CHANGED",
3713: "FOG_CHANGED", "BOUNDINGLEAF_CHANGED", "SHAPE3D_CHANGED",
3714: "TEXT3D_TRANSFORM_CHANGED", "TEXT3D_DATA_CHANGED",
3715: "SWITCH_CHANGED", "COND_MET", "BEHAVIOR_ENABLE",
3716: "BEHAVIOR_DISABLE", "INSERT_RENDERATOMS",
3717: "ORDERED_GROUP_INSERTED", "ORDERED_GROUP_REMOVED",
3718: "COLLISION_BOUND_CHANGED", "REGION_BOUND_CHANGED",
3719: "MODELCLIP_CHANGED", "BOUNDS_AUTO_COMPUTE_CHANGED",
3720: "SOUND_ATTRIB_CHANGED", "AURALATTRIBUTES_CHANGED",
3721: "SOUNDSCAPE_CHANGED", "ALTERNATEAPPEARANCE_CHANGED",
3722: "RENDER_OFFSCREEN", "RENDER_RETAINED", "RENDER_IMMEDIATE",
3723: "SOUND_STATE_CHANGED", "ORIENTEDSHAPE3D_CHANGED",
3724: "TEXTURE_UNIT_STATE_CHANGED", "UPDATE_VIEWPLATFORM",
3725: "BEHAVIOR_ACTIVATE", "GEOMETRYARRAY_CHANGED",
3726: "MEDIA_CONTAINER_CHANGED", "RESIZE_CANVAS",
3727: "TOGGLE_CANVAS", "IMAGE_COMPONENT_CHANGED",
3728: "SCHEDULING_INTERVAL_CHANGED", "VIEWSPECIFICGROUP_CHANGED",
3729: "VIEWSPECIFICGROUP_INIT", "VIEWSPECIFICGROUP_CLEAR",
3730: "ORDERED_GROUP_TABLE_CHANGED", "BEHAVIOR_REEVALUATE",
3731: "CREATE_OFFSCREENBUFFER",
3732: "DESTROY_CTX_AND_OFFSCREENBUFFER",
3733: "SHADER_ATTRIBUTE_CHANGED", "SHADER_ATTRIBUTE_SET_CHANGED",
3734: "SHADER_APPEARANCE_CHANGED", "ALLOCATE_CANVASID",
3735: "FREE_CANVASID", };
3736:
3737: private String dumpThreads(int threads) {
3738: StringBuffer strBuf = new StringBuffer();
3739: strBuf.append("threads:");
3740: //dump Threads type
3741: if ((threads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
3742: strBuf.append(" BEHAVIOR_SCHEDULER");
3743: }
3744: if ((threads & J3dThread.SOUND_SCHEDULER) != 0) {
3745: strBuf.append(" SOUND_SCHEDULER");
3746: }
3747: if ((threads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
3748: strBuf.append(" INPUT_DEVICE_SCHEDULER");
3749: }
3750: if ((threads & J3dThread.RENDER_THREAD) != 0) {
3751: strBuf.append(" RENDER_THREAD");
3752: }
3753: if ((threads & J3dThread.UPDATE_GEOMETRY) != 0) {
3754: strBuf.append(" UPDATE_GEOMETRY");
3755: }
3756: if ((threads & J3dThread.UPDATE_RENDER) != 0) {
3757: strBuf.append(" UPDATE_RENDER");
3758: }
3759: if ((threads & J3dThread.UPDATE_BEHAVIOR) != 0) {
3760: strBuf.append(" UPDATE_BEHAVIOR");
3761: }
3762: if ((threads & J3dThread.UPDATE_SOUND) != 0) {
3763: strBuf.append(" UPDATE_SOUND");
3764: }
3765: if ((threads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
3766: strBuf.append(" UPDATE_RENDERING_ATTRIBUTES");
3767: }
3768: if ((threads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
3769: strBuf.append(" UPDATE_RENDERING_ENVIRONMENT");
3770: }
3771: if ((threads & J3dThread.UPDATE_TRANSFORM) != 0) {
3772: strBuf.append(" UPDATE_TRANSFORM");
3773: }
3774:
3775: return strBuf.toString();
3776: }
3777:
3778: // Method to log the specified message. Note that the caller
3779: // should check for isCoreLoggable(FINEST) before calling
3780: private void dumpMessage(String str, J3dMessage m) {
3781: StringBuffer strBuf = new StringBuffer();
3782: strBuf.append(str).append(" ");
3783: if (m.type >= 0 && m.type < mtype.length) {
3784: strBuf.append(mtype[m.type]);
3785: } else {
3786: strBuf.append("<UNKNOWN>");
3787: }
3788: strBuf.append(" ").append(dumpThreads(m.threads));
3789: getCoreLogger().finest(strBuf.toString());
3790: }
3791:
3792: int frameCount = 0;
3793: private int frameCountCutoff = 100;
3794:
3795: private void manageMemory() {
3796: if (++frameCount > frameCountCutoff) {
3797: FreeListManager.manageLists();
3798: frameCount = 0;
3799: }
3800: }
3801:
3802: /**
3803: * Yields the current thread, by sleeping for a small amount of
3804: * time. Unlike <code>Thread.yield()</code>, this method
3805: * guarantees that the current thread will yield to another thread
3806: * waiting to run. It also ensures that the other threads will
3807: * run for at least a small amount of time before the current
3808: * thread runs again.
3809: */
3810: static final void threadYield() {
3811: // Note that we can't just use Thread.yield(), since it
3812: // doesn't guarantee that it will actually yield the thread
3813: // (and, in fact, it appears to be a no-op under Windows). So
3814: // we will sleep for 1 msec instead. Since most threads use
3815: // wait/notify, and only use this when they are waiting for
3816: // another thread to finish something, this shouldn't be a
3817: // performance concern.
3818:
3819: //Thread.yield();
3820: try {
3821: Thread.sleep(1);
3822: } catch (InterruptedException e) {
3823: // Do nothing, since we really don't care how long (or
3824: // even whether) we sleep
3825: }
3826: }
3827:
3828: // Return the number of available processors
3829: private int getNumberOfProcessors() {
3830: return Runtime.getRuntime().availableProcessors();
3831: }
3832:
3833: //
3834: // The following framework supports code instrumentation. To use it,
3835: // add code of the following form to areas that you want to enable for
3836: // timing:
3837: //
3838: // long startTime = System.nanoTime();
3839: // sortTransformGroups(tSize, tgs);
3840: // long deltaTime = System.nanoTime() - startTime;
3841: // VirtualUniverse.mc.recordTime(MasterControl.TimeType.XXXXX, deltaTime);
3842: //
3843: // where "XXXXX" is the enum representing the code segment being timed.
3844: // Additional enums can be defined for new subsystems.
3845: //
3846:
3847: static enum TimeType {
3848: TOTAL_FRAME, RENDER, BEHAVIOR,
3849: // TRANSFORM_UPDATE,
3850: // ...
3851: }
3852:
3853: private long[] statTimes = new long[TimeType.values().length];
3854: private int[] statCounts = new int[TimeType.values().length];
3855: private boolean[] statSeen = new boolean[TimeType.values().length];
3856: private int frameCycleTick = 0;
3857: private long frameCycleNumber = 0L;
3858:
3859: // Method to record times -- should not be called unless the stats logger
3860: // level is set to INFO or lower
3861: synchronized void recordTime(TimeType type, long deltaTime) {
3862: int idx = type.ordinal();
3863: statTimes[idx] += deltaTime;
3864: statCounts[idx]++;
3865: statSeen[idx] = true;
3866: }
3867:
3868: // Method to record times -- this is not called unless the stats logger
3869: // level is set to INFO or lower
3870: private synchronized void logTimes() {
3871: ++frameCycleNumber;
3872: if (++frameCycleTick >= 10) {
3873: StringBuffer strBuf = new StringBuffer();
3874: strBuf.append(
3875: "----------------------------------------------\n")
3876: .append(" Frame Number = ").append(
3877: frameCycleNumber).append("\n");
3878: for (int i = 0; i < statTimes.length; i++) {
3879: if (statSeen[i]) {
3880: strBuf.append(" ");
3881: if (statCounts[i] > 0) {
3882: strBuf
3883: .append(TimeType.values()[i])
3884: .append(" [")
3885: .append(statCounts[i])
3886: .append("] = ")
3887: .append(
3888: (double) statTimes[i]
3889: / 1000000.0
3890: / (double) statCounts[i])
3891: .append(" msec per call\n");
3892: statTimes[i] = 0L;
3893: statCounts[i] = 0;
3894: } else {
3895: assert statTimes[i] == 0L;
3896: strBuf.append(TimeType.values()[i]).append(
3897: " [0] = 0.0 msec\n");
3898: }
3899: }
3900: }
3901: getStatsLogger().info(strBuf.toString());
3902: frameCycleTick = 0;
3903: }
3904: }
3905:
3906: }
|