1 /*
2  * $RCSfile: MasterControl.java,v $
3  *
4  * Copyright 1998-2008 Sun Microsystems, Inc.  All Rights Reserved.
5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6  *
7  * This code is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 only, as
9  * published by the Free Software Foundation.  Sun designates this
10  * particular file as subject to the "Classpath" exception as provided
11  * by Sun in the LICENSE file that accompanied this code.
12  *
13  * This code is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  * version 2 for more details (a copy is included in the LICENSE file that
17  * accompanied this code).
18  *
19  * You should have received a copy of the GNU General Public License version
20  * 2 along with this work; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24  * CA 95054 USA or visit www.sun.com if you need additional information or
25  * have any questions.
26  *
27  * $Revision: 1.40 $
28  * $Date: 2008/05/22 18:10:39 $
29  * $State: Exp $
30  */
31 
32 /*
33  * Portions of this code were derived from work done by the Blackdown
34  * group (www.blackdown.org), who did the initial Linux implementation
35  * of the Java 3D API.
36  */
37 
38 package javax.media.j3d;
39 
40 import java.util.*;
41 import java.awt.*;
42 import java.util.logging.Level;
43 import java.util.logging.Logger;
44 
45 class MasterControl {
46 
47     /**
48      * Options for the runMonitor
49      */
50     static final int CHECK_FOR_WORK = 0;
51     static final int SET_WORK       = 1;
52     static final int RUN_THREADS    = 2;
53     static final int THREAD_DONE    = 3;
54     static final int SET_WORK_FOR_REQUEST_RENDERER   = 5;
55     static final int RUN_RENDERER_CLEANUP            = 6;
56 
57     // The thread states for MC
58     static final int SLEEPING            = 0;
59     static final int RUNNING             = 1;
60     static final int WAITING_FOR_THREADS = 3;
61     static final int WAITING_FOR_CPU     = 4;
62     static final int WAITING_FOR_RENDERER_CLEANUP = 5;
63 
64     // Constants used in renderer thread argument
65     static final Integer REQUESTRENDER = new Integer(Renderer.REQUESTRENDER);
66     static final Integer RENDER = new Integer(Renderer.RENDER);
67     static final Integer SWAP = new Integer(Renderer.SWAP);
68 
69     // Constants used for request from user threads
70     static final Integer ACTIVATE_VIEW = new Integer(1);
71     static final Integer DEACTIVATE_VIEW = new Integer(2);
72     static final Integer START_VIEW = new Integer(3);
73     static final Integer STOP_VIEW = new Integer(4);
74     static final Integer REEVALUATE_CANVAS = new Integer(5);
75     static final Integer UNREGISTER_VIEW = new Integer(6);
76     static final Integer PHYSICAL_ENV_CHANGE = new Integer(7);
77     static final Integer INPUTDEVICE_CHANGE = new Integer(8);
78     static final Integer EMPTY_UNIVERSE = new Integer(9);
79     static final Integer START_RENDERER = new Integer(10);
80     static final Integer STOP_RENDERER = new Integer(11);
81     static final Integer RENDER_ONCE = new Integer(12);
82     static final Integer FREE_CONTEXT = new Integer(13);
83     static final Integer FREE_DRAWING_SURFACE = new Integer(14);
84     static final Integer FREE_MESSAGE = new Integer(15);
85     static final Integer RESET_CANVAS = new Integer(16);
86     static final Integer GETBESTCONFIG = new Integer(17);
87     static final Integer ISCONFIGSUPPORT = new Integer(18);
88     static final Integer SET_GRAPHICSCONFIG_FEATURES = new Integer(19);
89     static final Integer SET_QUERYPROPERTIES = new Integer(20);
90     static final Integer SET_VIEW = new Integer(21);
91 
92     // Developer logger for reporting informational messages; see getDevLogger()
93     private static boolean devLoggerEnabled = false;
94     private static Logger devLogger;
95 
96     // Stats logger for reporting runtime statistics; see getStatsLogger()
97     private static boolean statsLoggerEnabled = false;
98     private static Logger statsLogger;
99 
100     // Core logger for reporting internal errors, warning, and
101     // informational messages; see getCoreLogger()
102     private static boolean coreLoggerEnabled = false;
103     private static Logger coreLogger;
104 
105     // Flag indicating that the rendering pipeline libraries are loaded
106     private static boolean librariesLoaded = false;
107 
108     // Issue 257: flag indicating that we are running in "appletLauncher" mode
109     // and should use JNLPAppletLauncher to load any native libraries
110     private static boolean appletLauncher = false;
111 
112     /**
113      * reference to MasterControl thread
114      */
115     private MasterControlThread mcThread = null;
116 
117     /**
118      * The list of views that are currently registered
119      */
120     private UnorderList views = new UnorderList(1, View.class);
121 
122 
123     /**
124      * by MIK OF CLASSX
125      *
126      * the flag to indicate whether the background of the offscreen
127      * canvas must be transparent or not false by default
128      */
129     boolean transparentOffScreen = false;
130 
131     /**
132      * Flag to indicate whether Pbuffers are used for off-screen
133      * rendering; true by default.  Set by the "j3d.usePbuffer"
134      * property, When this flag is set to false, Bitmap (Windows) or
135      * Pixmap (UNIX) rendering will be used
136      */
137     boolean usePbuffer = true;
138 
139     /**
140      * Flag to indicate whether should renderer view frustum culling is done;
141      * true by default.
142      * Set by the -Dj3d.viewFrustumCulling property, When this flag is
143      * set to false, the renderer view frustum culling is turned off.
144      */
145     boolean viewFrustumCulling = true;
146 
147     /**
148      * the flag to indicate whether the geometry should be locked or not
149      */
150 
151     private boolean lockGeometry = false;
152 
153     /**
154      * The number of registered views that are active
155      */
156     private int numActiveViews = 0;
157 
158     /**
159      * The list of active universes get from View
160      */
161     private UnorderList activeUniverseList = new UnorderList(VirtualUniverse.class);
162 
163     /**
164      * The list of universes register from View
165      */
166     private UnorderList regUniverseList = new UnorderList(VirtualUniverse.class);
167 
168     /**
169      * A lock used for accessing time structures.
170      */
171     private Object timeLock = new Object();
172 
173 
174     /**
175      * The current "time" value
176      */
177     private long time = 0;
178 
179     /**
180      * Use to assign threadOpts in Renderer thread.
181      */
182     private long waitTimestamp = 0;
183 
184     /**
185      * The current list of work threads
186      */
187     private UnorderList stateWorkThreads =
188                                new UnorderList(J3dThreadData.class);
189     private UnorderList renderWorkThreads =
190                                new UnorderList(J3dThreadData.class);
191     private UnorderList requestRenderWorkThreads =
192 			       new UnorderList(J3dThreadData.class);
193 
194     /**
195      * The current list of work threads
196      */
197     private UnorderList renderThreadData = new UnorderList(J3dThreadData.class);
198 
199     /**
200      * The list of input device scheduler thread
201      */
202     private UnorderList inputDeviceThreads =
203                              new UnorderList(1, InputDeviceScheduler.class);
204 
205     /**
206      * A flag that is true when the thread lists need updating
207      */
208     private boolean threadListsChanged;
209 
210 
211     /**
212      * Markers for the last transform structure update thread
213      * and the last update thread.
214      */
215     private int lastTransformStructureThread = 0;
216     private int lastStructureUpdateThread = 0;
217 
218     /**
219      * The current time snapshots
220      */
221     private long currentTime;
222 
223     // Only one Timer thread in the system.
224     TimerThread timerThread;
225 
226     // Only one Notification thread in the system.
227     private NotificationThread notificationThread;
228 
229     /**
230      * This flag indicates that MC is running
231      */
232     volatile boolean running = true;
233 
234     /**
235      * This flag indicates that MC has work to do
236      */
237     private boolean workToDo = false;
238 
239     /**
240      * This flag indicates that there is work for requestRenderer
241      */
242     private boolean requestRenderWorkToDo = false;
243 
244    /**
245      * The number of THREAD_DONE messages pending
246      */
247     private int threadPending = 0;
248     private int renderPending = 0;
249     private int statePending = 0;
250 
251     /**
252      * State variables for work lists
253      */
254     private boolean renderWaiting = false;
255     private boolean stateWaiting = false;
256 
257     /**
258      * The current state of the MC thread
259      */
260     private int state = SLEEPING;
261 
262     // time for sleep in order to met the minimum frame duration
263     private long sleepTime = 0;
264 
265 
266     /**
267      * The number of cpu's Java 3D may use
268      */
269     private int cpuLimit;
270 
271     /**
272      * A list of mirror objects to be updated
273      */
274     private UnorderList mirrorObjects = new UnorderList(ObjectUpdate.class);
275 
276     /**
277      * The renderingAttributesStructure for updating node component
278      * objects
279      */
280     private RenderingAttributesStructure renderingAttributesStructure =
281                         new RenderingAttributesStructure();
282 
283     /**
284      * The default rendering method
285      */
286     private DefaultRenderMethod defaultRenderMethod = null;
287 
288     /**
289      * The text3D rendering method
290      */
291     private Text3DRenderMethod text3DRenderMethod = null;
292 
293     /**
294      * The vertex array rendering method
295      */
296     private VertexArrayRenderMethod vertexArrayRenderMethod = null;
297 
298     /**
299      * The displayList rendering method
300      */
301     private DisplayListRenderMethod displayListRenderMethod = null;
302 
303     /**
304      * The compressed geometry rendering method
305      */
306     private CompressedGeometryRenderMethod compressedGeometryRenderMethod = null;
307 
308     /**
309      * The oriented shape3D rendering method
310      */
311     private OrientedShape3DRenderMethod orientedShape3DRenderMethod = null;
312 
313     /**
314      * This is the start time upon which alpha's and behaviors
315      * are synchronized to. It is initialized once, the first time
316      * that a MasterControl object is created.
317      */
318     static long systemStartTime = 0L;
319 
320     // Flag indicating that we are on a Windows OS
321     private static boolean isWindowsOs = false;
322 
323     // Flag indicating we are on MacOS
324     private static boolean isMacOs = false;
325 
326     // This is a counter for texture id's, valid id starts from 1
327     private int textureIdCount = 0;
328 
329     // This is lock for both 2D/3D textureIds;
330     private Object textureIdLock = new Object();
331 
332     // This is a time stamp used when context is created
333     private long contextTimeStamp = 0;
334 
335     // This is an array of  canvasIds in used
336     private boolean[] canvasIds = null;
337     private int canvasFreeIndex = 0;
338     private Object canvasIdLock = new Object();
339 
340     // This is a counter for rendererBit
341     private int rendererCount = 0;
342 
343     // Flag that indicates whether to shared display context or not
344     boolean isSharedCtx = false;
345 
346     // Flag that tells us to use NV_register_combiners
347     boolean useCombiners = false;
348 
349     // Flag that indicates whether compile is disabled or not
350     boolean disableCompile = false;
351 
352     // Flag that indicates whether or not compaction occurs
353     boolean doCompaction = true;
354 
355     // Flag that indicates whether separate specular color is disabled or not
356     boolean disableSeparateSpecularColor = false;
357 
358     // Flag that indicates whether DisplayList is used or not
359     boolean isDisplayList = true;
360 
361     // If this flag is set, then by-ref geometry will not be
362     // put in display list
363     boolean buildDisplayListIfPossible = false;
364 
365     // If this flag is set, then geometry arrays with vertex attributes can
366     // be in display list.
367     boolean vertexAttrsInDisplayList = false;
368 
369     // Issue 249 - flag that indicates whether the soleUser optimization is permitted
370     boolean allowSoleUser = false;
371 
372     // Issue 266 - Flag indicating whether null graphics configs are allowed
373     // Set by -Dj3d.allowNullGraphicsConfig property
374     // Setting this flag causes Canvas3D to allow a null GraphicsConfiguration
375     // for on-screen canvases. This is only for backward compatibility with
376     // legacy applications.
377     boolean allowNullGraphicsConfig = false;
378 
379     // Issue 239 - Flag indicating whether the stencil buffer is cleared by
380     // default each frame when the color and depth buffers are cleared.
381     // Note that this is a partial solution, since we eventually want an API
382     // to control this.
383     boolean stencilClear = false;
384 
385     // The global shading language being used. Using a ShaderProgram
386     // with a shading language other than the one specified by
387     // globalShadingLanguage will cause a ShaderError to be generated,
388     static int globalShadingLanguage = Shader.SHADING_LANGUAGE_GLSL;
389 
390     // Flags indicating whether the Cg or GLSL libraries are available; we still need
391     // to check for the actual extension support when the Canvas3D with its associated context
392     // is created. Note that these are qualifed by the above globalShadingLanguage, so at
393     // most one of these two flags will be true;
394     static boolean cgLibraryAvailable = false;
395     static boolean glslLibraryAvailable = false;
396 
397 
398     // REQUESTCLEANUP messages argument
399     static Integer REMOVEALLCTXS_CLEANUP = new Integer(1);
400     static Integer REMOVECTX_CLEANUP     = new Integer(2);
401     static Integer REMOVENOTIFY_CLEANUP  = new Integer(3);
402     static Integer RESETCANVAS_CLEANUP   = new Integer(4);
403     static Integer FREECONTEXT_CLEANUP   = new Integer(5);
404 
405     // arguments for renderer resource cleanup run
406     Object rendererCleanupArgs[] = {new Integer(Renderer.REQUESTCLEANUP),
407 				    null, null};
408 
409 
410     // Context creation should obtain this lock, so that
411     // first_time and all the extension initilialization
412     // are done in the MT safe manner
413     Object contextCreationLock = new Object();
414 
415     // Flag that indicates whether to lock the DSI while rendering
416     boolean doDsiRenderLock = false;
417 
418     // Flag that indicates the pre-1.5 behavior of enforcing power-of-two
419     // textures. If set, then any non-power-of-two textures will throw an
420     // exception.
421     boolean enforcePowerOfTwo = false;
422 
423     // Flag that indicates whether the framebuffer is sharing the
424     // Z-buffer with both the left and right eyes when in stereo mode.
425     // If this is true, we need to clear the Z-buffer between rendering
426     // to the left and right eyes.
427     boolean sharedStereoZBuffer = true;
428 
429     // True to disable all underlying multisampling API so it uses
430     // the setting in the driver.
431     boolean implicitAntialiasing = false;
432 
433     // False to disable compiled vertex array extensions if support
434     boolean isCompiledVertexArray = true;
435 
436     // Number of reserved vertex attribute locations for GLSL (must be at
437     // least 1).
438     // Issue 269 - need to reserve up to 6 vertex attribtue locations to ensure
439     // that we don't collide with a predefined gl_* attribute on nVidia cards.
440     int glslVertexAttrOffset = 6;
441 
442     // Hashtable that maps a GraphicsDevice to its associated
443     // Screen3D--this is only used for on-screen Canvas3Ds
444     Hashtable deviceScreenMap = new Hashtable();
445 
446     // Use to store all requests from user threads.
447     UnorderList requestObjList = new UnorderList();
448     private UnorderList requestTypeList = new UnorderList(Integer.class);
449 
450     // Temporary storage to store stop request for requestViewList
451     private UnorderList tempViewList = new UnorderList();
452     private UnorderList renderOnceList = new UnorderList();
453 
454     // This flag is true when there is pending request
455     // i.e. false when the above requestxxx Lists are all empty.
456     private boolean pendingRequest = false;
457 
458     // Root ThreadGroup for creating Java 3D threads
459     private static ThreadGroup rootThreadGroup;
460 
461     // Thread priority for all Java 3D threads
462     private static int threadPriority;
463 
464     static private Object mcThreadLock = new Object();
465 
466     private ArrayList timestampUpdateList = new ArrayList(3);
467 
468     private UnorderList freeMessageList = new UnorderList(8);
469 
470     // Native AWT object
471     long awt;
472 
473     // Maximum number of lights
474     int maxLights;
475 
476     // This is used for D3D only
477     int resendTexTimestamp = 0;
478 
479     // Indicates that the property to disable Xinerama mode was set and
480     // successfully disabled.
481     boolean xineramaDisabled = false;
482 
483     // Set by the -Dj3d.sortShape3DBounds property, When this flag is
484     // set to true, the bounds of the Shape3D node will be used in
485     // place of the computed GeometryArray bounds for transparency
486     // sorting for those Shape3D nodes whose boundsAutoCompute
487     // attribute is set to false.
488     boolean sortShape3DBounds = false;
489 
490     //Set by -Dj3d.forceReleaseView property.
491     //Setting this flag as true disables the bug fix 4267395 in View deactivate().
492     //The bug 4267395 can lock-up *some* systems, but the bug fix can
493     //produce memory leaks in applications which creates and destroy Canvas3D
494     //from time to time.
495     //Set as true if you have memory leaks after disposing Canvas3D.
496     //Default false value does affect Java3D View dispose behavior.
497     boolean forceReleaseView = false;
498 
499     // Issue 561: Set by -Dj3d.releaseBoundingBoxMemory property.
500     // When set to true, the per-instance fields used in bounding box
501     // transformation are released at the end of transform methods. This saves
502     // a significant amount of memory in large scenes containing huge amounts
503     // of bounding boxes. Setting this false can improve performance when
504     // lots of transforms are performed. The default is false.
505     boolean releaseBoundingBoxMemory = false;
506 
507     // Issue 480: Cache the bounds of nodes so that getBounds does not
508     // recompute the boounds of the entire graph per call
509     boolean cacheAutoComputedBounds = false;
510 
511     // issue 544
512     boolean useBoxForGroupBounds = false;
513 
514     /**
515      * Constructs a new MasterControl object.  Note that there is
516      * exatly one MasterControl object, created statically by
517      * VirtualUniverse.
518      */
MasterControl()519     MasterControl() {
520         assert librariesLoaded;
521 
522 	// Get AWT handle
523 	awt = Pipeline.getPipeline().getAWT();
524 
525         // Initialize the start time upon which alpha's and behaviors
526         // are synchronized to (if it isn't already set).
527         if (systemStartTime == 0L) {
528             systemStartTime = J3dClock.currentTimeMillis();
529         }
530 
531 	if(J3dDebug.devPhase) {
532 	    // Check to see whether debug mode is allowed
533 	    J3dDebug.debug = getBooleanProperty("j3d.debug", false,
534 						"J3dDebug.debug");
535 	}
536 
537 	// Check to see whether shared contexts are allowed
538 	if (!isD3D()) {
539 	    isSharedCtx =
540 		getBooleanProperty("j3d.sharedctx", isSharedCtx, "shared contexts");
541 	}
542 
543 	doCompaction = getBooleanProperty("j3d.docompaction", doCompaction,
544 					  "compaction");
545 
546 	// by MIK OF CLASSX
547 	transparentOffScreen = getBooleanProperty("j3d.transparentOffScreen", transparentOffScreen, "transparent OffScreen");
548 
549 	usePbuffer = getBooleanProperty("j3d.usePbuffer",
550 					usePbuffer,
551 					"Off-screen Pbuffer");
552 
553 	viewFrustumCulling = getBooleanProperty("j3d.viewFrustumCulling", viewFrustumCulling,"View frustum culling in the renderer is");
554 
555 	sortShape3DBounds =
556 	    getBooleanProperty("j3d.sortShape3DBounds", sortShape3DBounds,
557 			       "Shape3D bounds enabled for transparency sorting",
558 			       "Shape3D bounds *ignored* for transparency sorting");
559 
560 	forceReleaseView =
561 	    getBooleanProperty("j3d.forceReleaseView", forceReleaseView,
562 			       "forceReleaseView  after Canvas3D dispose enabled",
563 			       "forceReleaseView  after Canvas3D dispose disabled");
564 
565         releaseBoundingBoxMemory = getBooleanProperty("j3d.releaseBoundingBoxMemory",
566                 releaseBoundingBoxMemory, "releasing memory after bounding box transform");
567 
568 	useCombiners = getBooleanProperty("j3d.usecombiners", useCombiners,
569 					  "Using NV_register_combiners if available",
570 					  "NV_register_combiners disabled");
571 
572 	if (getProperty("j3d.disablecompile") != null) {
573 	    disableCompile = true;
574 	    System.err.println("Java 3D: BranchGroup.compile disabled");
575 	}
576 
577 	if (getProperty("j3d.disableSeparateSpecular") != null) {
578 	    disableSeparateSpecularColor = true;
579 	    System.err.println("Java 3D: separate specular color disabled if possible");
580 	}
581 
582 	isDisplayList = getBooleanProperty("j3d.displaylist", isDisplayList,
583 					   "display list");
584 
585 	implicitAntialiasing =
586 	    getBooleanProperty("j3d.implicitAntialiasing",
587 			       implicitAntialiasing,
588 			       "implicit antialiasing");
589 
590 	isCompiledVertexArray =
591 	    getBooleanProperty("j3d.compiledVertexArray",
592 			       isCompiledVertexArray,
593 			       "compiled vertex array");
594 
595         boolean j3dOptimizeSpace =
596 	    getBooleanProperty("j3d.optimizeForSpace", true,
597 			       "optimize for space");
598 
599         if (isDisplayList) {
600             // Build Display list for by-ref geometry
601             // ONLY IF optimizeForSpace is false
602             if (!j3dOptimizeSpace) {
603                 buildDisplayListIfPossible = true;
604             }
605 
606             // Build display lists for geometry with vertex attributes
607             // ONLY if we are in GLSL mode and GLSL shaders are available
608             if (glslLibraryAvailable) {
609                 vertexAttrsInDisplayList = true;
610             }
611         }
612 
613         // Check to see whether Renderer can run without DSI lock
614 	doDsiRenderLock = getBooleanProperty("j3d.renderLock",
615 					     doDsiRenderLock,
616 					     "render lock");
617 
618         // Check to see whether we enforce power-of-two textures
619         enforcePowerOfTwo = getBooleanProperty("j3d.textureEnforcePowerOfTwo",
620 					       enforcePowerOfTwo,
621 					       "checking power-of-two textures");
622 
623         // Issue 249 - check to see whether the soleUser optimization is permitted
624         allowSoleUser = getBooleanProperty("j3d.allowSoleUser",
625 					   allowSoleUser,
626 					   "sole-user mode");
627 
628         // Issue 266 - check to see whether null graphics configs are allowed
629         allowNullGraphicsConfig = getBooleanProperty("j3d.allowNullGraphicsConfig",
630 						     allowNullGraphicsConfig,
631 						     "null graphics configs");
632 
633         // Issue 239 - check to see whether per-frame stencil clear is enabled
634         stencilClear = getBooleanProperty("j3d.stencilClear",
635                                           stencilClear,
636                                           "per-frame stencil clear");
637 
638         // Check to see if stereo mode is sharing the Z-buffer for both eyes.
639 	sharedStereoZBuffer =
640 	    getBooleanProperty("j3d.sharedstereozbuffer",
641 			       sharedStereoZBuffer,
642 			       "shared stereo Z buffer");
643 
644 	// Get the maximum number of concurrent threads (CPUs)
645 	final int defaultThreadLimit = getNumberOfProcessors() + 1;
646 	Integer threadLimit =
647 	    (Integer) java.security.AccessController.doPrivileged(
648 	    new java.security.PrivilegedAction() {
649 		public Object run() {
650 		    return Integer.getInteger("j3d.threadLimit",
651 					      defaultThreadLimit);
652 		}
653 	    });
654 
655 	cpuLimit = threadLimit.intValue();
656 	if (cpuLimit < 1)
657 	    cpuLimit = 1;
658 	if (J3dDebug.debug || cpuLimit != defaultThreadLimit) {
659 	    System.err.println("Java 3D: concurrent threadLimit = " +
660 			       cpuLimit);
661 	}
662 
663 	// Get the input device scheduler sampling time
664 	Integer samplingTime  =
665 	    (Integer) java.security.AccessController.doPrivileged(
666 	    new java.security.PrivilegedAction() {
667 		public Object run() {
668 		    return Integer.getInteger("j3d.deviceSampleTime", 0);
669 		}
670 	    });
671 
672 	if (samplingTime.intValue() > 0) {
673 	    InputDeviceScheduler.samplingTime =
674 		samplingTime.intValue();
675 	    System.err.println("Java 3D: Input device sampling time = "
676 			       + samplingTime + " ms");
677 	}
678 
679 	// Get the glslVertexAttrOffset
680 	final int defaultGLSLVertexAttrOffset = glslVertexAttrOffset;
681 	Integer vattrOffset =
682 	    (Integer) java.security.AccessController.doPrivileged(
683 	    new java.security.PrivilegedAction() {
684 		public Object run() {
685 		    return Integer.getInteger("j3d.glslVertexAttrOffset",
686 					      defaultGLSLVertexAttrOffset);
687 		}
688 	    });
689 
690 	glslVertexAttrOffset = vattrOffset.intValue();
691         if (glslVertexAttrOffset < 1) {
692             glslVertexAttrOffset = 1;
693         }
694 	if (J3dDebug.debug || glslVertexAttrOffset != defaultGLSLVertexAttrOffset) {
695 	    System.err.println("Java 3D: glslVertexAttrOffset = " +
696 			       glslVertexAttrOffset);
697 	}
698 
699         // See if Xinerama should be disabled for better performance.
700 	boolean disableXinerama = false;
701 	if (getProperty("j3d.disableXinerama") != null) {
702 	    disableXinerama = true;
703 	}
704 
705         // Issue 480 : Cache bounds returned by getBounds()
706         cacheAutoComputedBounds =
707                 getBooleanProperty("j3d.cacheAutoComputeBounds",
708                 cacheAutoComputedBounds,
709                 "Cache AutoCompute Bounds, accelerates getBounds()");
710 
711         // Issue 544
712         useBoxForGroupBounds =
713                 getBooleanProperty("j3d.useBoxForGroupBounds",
714                 useBoxForGroupBounds,
715                 "Use of BoundingBox for group geometric bounds");
716 
717         // Initialize the native J3D library
718 	if (!Pipeline.getPipeline().initializeJ3D(disableXinerama)) {
719 	    throw new RuntimeException(J3dI18N.getString("MasterControl0"));
720 	}
721 
722 	if (xineramaDisabled) {
723 	    // initializeJ3D() successfully disabled Xinerama.
724 	    System.err.println("Java 3D: Xinerama disabled");
725 	}
726 	else if (disableXinerama) {
727 	    // j3d.disableXinerama is true, but attempt failed.
728 	    System.err.println("Java 3D: could not disable Xinerama");
729 	}
730 
731         // Check for obsolete properties
732         String[] obsoleteProps = {
733             "j3d.backgroundtexture",
734             "j3d.forceNormalized",
735             "j3d.g2ddrawpixel",
736             "j3d.simulatedMultiTexture",
737             "j3d.useFreeLists",
738         };
739         for (int i = 0; i < obsoleteProps.length; i++) {
740             if (getProperty(obsoleteProps[i]) != null) {
741                 System.err.println("Java 3D: " + obsoleteProps[i] + " property ignored");
742             }
743         }
744 
745 	// Get the maximum Lights
746 	maxLights = Pipeline.getPipeline().getMaximumLights();
747 
748 	// create the freelists
749 	FreeListManager.createFreeLists();
750 
751 	// create an array canvas use registers
752 	// The 32 limit can be lifted once the
753 	// resourceXXXMasks in other classes
754 	// are change not to use integer.
755 	canvasIds = new boolean[32];
756 	for(int i=0; i<canvasIds.length; i++) {
757 	    canvasIds[i] = false;
758 	}
759         canvasFreeIndex = 0;
760     }
761 
initLogger(Logger logger, Level defaultLevel)762     private static boolean initLogger(Logger logger, Level defaultLevel) {
763         if (logger == null) {
764             return false;
765         }
766 
767         if (defaultLevel != null &&
768                 logger.getLevel() == null &&
769                 Logger.getLogger("j3d").getLevel() == null) {
770 
771             try {
772                 // Set default logger level rather than inheriting from system global
773                 logger.setLevel(defaultLevel);
774             } catch (SecurityException ex) {
775                 System.err.println(ex);
776                 return false;
777             }
778         }
779 
780         return logger.isLoggable(Level.SEVERE);
781     }
782 
783     // Called by the static initializer to initialize the loggers
initLoggers()784     private static void initLoggers() {
785         coreLogger = Logger.getLogger("j3d.core");
786         devLogger = Logger.getLogger("j3d.developer");
787         statsLogger = Logger.getLogger("j3d.stats");
788 
789         java.security.AccessController.doPrivileged(
790             new java.security.PrivilegedAction() {
791                 public Object run() {
792                     coreLoggerEnabled = initLogger(coreLogger, null);
793                     devLoggerEnabled = initLogger(devLogger, Level.OFF);
794                     statsLoggerEnabled = initLogger(statsLogger, Level.OFF);
795                     return null;
796                 }
797         });
798     }
799 
800     /**
801      * Get the developer logger -- OFF by default
802      *
803      * WARNING - for probable incorrect or inconsistent api usage
804      * INFO - for informational messages such as performance hints (less verbose than FINE)
805      * FINE - for informational messages from inner loops
806      * FINER - using default values which may not be optimal
807      */
getDevLogger()808     static Logger getDevLogger() {
809         return devLogger;
810     }
811 
isDevLoggable(Level level)812     static boolean isDevLoggable(Level level) {
813         return devLoggerEnabled && devLogger.isLoggable(level);
814     }
815 
816     /**
817      * Get the stats logger -- OFF by default
818      *
819      * WARNING - statistical anomalies
820      * INFO - basic performance stats - not too verbose and minimally intrusive
821      * FINE - somewhat verbose and intrusive
822      * FINER - more verbose and intrusive
823      * FINEST - most verbose and intrusive
824      */
getStatsLogger()825     static Logger getStatsLogger() {
826         return statsLogger;
827     }
828 
isStatsLoggable(Level level)829     static boolean isStatsLoggable(Level level) {
830         return statsLoggerEnabled && statsLogger.isLoggable(level);
831     }
832 
833     /**
834      * Get the core logger -- level is INFO by default
835      *
836      * SEVERE - Serious internal errors
837      * WARNING - Possible internal errors or anomalies
838      * INFO - General informational messages
839      * FINE - Internal debugging information - somewhat verbose
840      * FINER - Internal debugging information - more verbose
841      * FINEST - Internal debugging information - most verbose
842      */
getCoreLogger()843     static Logger getCoreLogger() {
844         return coreLogger;
845     }
846 
isCoreLoggable(Level level)847     static boolean isCoreLoggable(Level level) {
848         return coreLoggerEnabled && coreLogger.isLoggable(level);
849     }
850 
getProperty(final String prop)851     private static String getProperty(final String prop) {
852 	return (String) java.security.AccessController.doPrivileged(
853 	    new java.security.PrivilegedAction() {
854 		public Object run() {
855 		    return System.getProperty(prop);
856 		}
857    	    });
858     }
859 
860     static boolean getBooleanProperty(String prop,
861 					      boolean defaultValue,
862 					      String trueMsg,
863 					      String falseMsg) {
864 	boolean value = defaultValue;
865 	String propValue = getProperty(prop);
866 
867 	if (propValue != null) {
868 	    value = Boolean.valueOf(propValue).booleanValue();
869 	    System.err.println("Java 3D: " + (value ? trueMsg : falseMsg));
870 	}
871 	return value;
872     }
873 
874     static boolean getBooleanProperty(String prop,
875 					      boolean defaultValue,
876 					      String msg) {
877 	return getBooleanProperty(prop,
878 				  defaultValue,
879 				  (msg + " enabled"),
880 				  (msg + " disabled"));
881     }
882 
883     /**
884      * Method to create and initialize the rendering Pipeline object,
885      * and to load the native libraries needed by Java 3D. This is
886      * called by the static initializer in VirtualUniverse <i>before</i>
887      * the MasterControl object is created.
888      */
889     static void loadLibraries() {
890         assert !librariesLoaded;
891 
892         // Get platform system properties
893         String osName = getProperty("os.name").toLowerCase();
894         String sunArchDataModel = getProperty("sun.arch.data.model");
895 
896         // Set global flags based on platform architecture
897         isMacOs = osName != null && osName.startsWith("mac");
898         isWindowsOs = osName != null && osName.startsWith("windows");
899         boolean isWindowsVista = isWindowsOs && osName.indexOf("vista") != -1;
900         boolean is64Bit = (sunArchDataModel != null) && sunArchDataModel.equals("64");
901 
902         // Issue 257: check to see if the sun.jnlp.applet.launcher property is set to true
903         String sunAppletLauncher = getProperty("sun.jnlp.applet.launcher");
904         appletLauncher = Boolean.valueOf(sunAppletLauncher);
905 
906         if (isCoreLoggable(Level.CONFIG)) {
907             StringBuffer strBuf = new StringBuffer();
908             strBuf.append("MasterControl.loadLibraries()\n").
909                     append("    osName [lower-case] = \"").
910                     append(osName).
911                     append("\"").
912                     append(", sunArchDataModel = ").
913                     append(sunArchDataModel).
914                     append("\n").
915                     append("    is64Bit = ").
916                     append(is64Bit).
917                     append(", isWindowsOs = ").
918                     append(isWindowsOs).
919                     append(", isMacOs = ").
920                     append(isMacOs).
921                     append(", isWindowsVista = ").
922                     append(isWindowsVista);
923             getCoreLogger().config(strBuf.toString());
924         }
925 
926         // Initialize the Pipeline object associated with the
927         // renderer specified by the "j3d.rend" system property.
928         //
929         // XXXX : We should consider adding support for a more flexible,
930         // dynamic selection scheme via an API call.
931 
932         // Default rendering pipeline is the JOGL pipeline on MacOS and the
933         // native OpenGL pipeline on all other platforms.
934         Pipeline.Type pipelineType =
935                 isMacOs ? Pipeline.Type.JOGL : Pipeline.Type.NATIVE_OGL;
936 
937         final String rendStr = getProperty("j3d.rend");
938         boolean nativeOglRequested = false;
939         if (rendStr == null) {
940             // Use default pipeline
941         } else if (rendStr.equals("ogl") && !isMacOs) {
942             pipelineType = Pipeline.Type.NATIVE_OGL;
943             nativeOglRequested = true;
944         } else if (rendStr.equals("d3d") && isWindowsOs) {
945             pipelineType = Pipeline.Type.NATIVE_D3D;
946         } else if (rendStr.equals("jogl")) {
947             pipelineType = Pipeline.Type.JOGL;
948         } else if (rendStr.equals("noop")) {
949             pipelineType = Pipeline.Type.NOOP;
950         } else {
951             System.err.println("Java 3D: Unrecognized renderer: " + rendStr);
952             // Use default pipeline
953         }
954 
955         // Issue 452 : if we are on 32-bit Windows, then check whether we
956         // can and should use OpenGL. Note that we can't do this on 64-bit
957         // Windows until we have a 64-bit D3D pipeline.
958         if (isWindowsOs && !is64Bit && pipelineType == Pipeline.Type.NATIVE_OGL) {
959             if (!Pipeline.useNativeOgl(isWindowsVista, nativeOglRequested)) {
960                 pipelineType = Pipeline.Type.NATIVE_D3D;
961             }
962         }
963 
964         // Construct the singleton Pipeline instance
965         Pipeline.createPipeline(pipelineType);
966 
967         // Get the global j3d.shadingLanguage system property
968         final String slStr = getProperty("j3d.shadingLanguage");
969         if (slStr != null) {
970             boolean found = false;
971             if (slStr.equals("GLSL")) {
972                 globalShadingLanguage = Shader.SHADING_LANGUAGE_GLSL;
973                 found = true;
974             } else if (slStr.equals("Cg")) {
975                 globalShadingLanguage = Shader.SHADING_LANGUAGE_CG;
976                 found = true;
977             }
978 
979             if (found) {
980                 System.err.println("Java 3D: Setting global shading language to " + slStr);
981             } else {
982                 System.err.println("Java 3D: Unrecognized shading language: " + slStr);
983             }
984         }
985 
986         // Load all required libraries
987         Pipeline.getPipeline().loadLibraries(globalShadingLanguage);
988 
989         // Check whether the Cg library is available
990         if (globalShadingLanguage == Shader.SHADING_LANGUAGE_CG) {
991             cgLibraryAvailable = Pipeline.getPipeline().isCgLibraryAvailable();
992         }
993 
994         // Check whether the GLSL library is available
995         if (globalShadingLanguage == Shader.SHADING_LANGUAGE_GLSL) {
996             glslLibraryAvailable = Pipeline.getPipeline().isGLSLLibraryAvailable();
997         }
998 
999         assert !(glslLibraryAvailable && cgLibraryAvailable) :
1000             "ERROR: cannot support both GLSL and CG at the same time";
1001 
1002         librariesLoaded = true;
1003     }
1004 
1005 
1006     /**
1007      * Invoke from InputDeviceScheduler to create an
1008      * InputDeviceBlockingThread.
1009      */
1010     InputDeviceBlockingThread getInputDeviceBlockingThread(
1011 					   final InputDevice device) {
1012 
1013 	return (InputDeviceBlockingThread)
1014 	    java.security.AccessController.doPrivileged(
1015 		 new java.security.PrivilegedAction() {
1016                      public Object run() {
1017 			 synchronized (rootThreadGroup) {
1018 			     Thread thread = new InputDeviceBlockingThread(
1019 				 	    rootThreadGroup, device);
1020 			     thread.setPriority(threadPriority);
1021 			     return thread;
1022 			 }
1023 		     }
1024 		 }
1025         );
1026     }
1027 
1028     /**
1029      * Set thread priority to all threads under Java3D thread group.
1030      */
1031     void setThreadPriority(final int pri) {
1032 	synchronized (rootThreadGroup) {
1033 	    threadPriority = pri;
1034 	    java.security.AccessController.doPrivileged(
1035                 new java.security.PrivilegedAction() {
1036                     public Object run() {
1037 			Thread list[] = new
1038 			    Thread[rootThreadGroup.activeCount()];
1039 			int count = rootThreadGroup.enumerate(list);
1040 			for (int i=count-1; i >=0; i--) {
1041 			    list[i].setPriority(pri);
1042 			}
1043 			return null;
1044 		    }
1045 	    });
1046 	}
1047     }
1048 
1049 
1050     /**
1051      * Return Java3D thread priority
1052      */
1053     int getThreadPriority() {
1054 	return threadPriority;
1055     }
1056 
1057     /**
1058      * This returns the a unused renderer bit
1059      */
1060     int getRendererBit() {
1061         return (1 << rendererCount++);
1062     }
1063 
1064 
1065     /**
1066      * This returns the a unused renderer bit
1067      */
1068     int getRendererId() {
1069         return rendererCount++;
1070     }
1071 
1072     /**
1073      * This returns a context creation time stamp
1074      * Note: this has to be called under the contextCreationLock
1075      */
1076     long getContextTimeStamp() {
1077 	return (++contextTimeStamp);
1078     }
1079 
1080 
1081     /**
1082      * This returns the a unused displayListId
1083      */
1084     Integer getDisplayListId() {
1085         return (Integer) FreeListManager.getObject(FreeListManager.DISPLAYLIST);
1086     }
1087 
1088     void freeDisplayListId(Integer id) {
1089 	FreeListManager.freeObject(FreeListManager.DISPLAYLIST, id);
1090     }
1091 
1092     /**
1093      * This returns the a unused textureId
1094      */
1095     int getTexture2DId() {
1096 	// MasterControl has to handle the ID itself.  2D and 3D ideas must
1097 	// never be the same, so the counter has to be in the MasterControl
1098 	MemoryFreeList textureIds =
1099 	    FreeListManager.getFreeList(FreeListManager.TEXTURE2D);
1100 	int id;
1101 
1102 	synchronized (textureIdLock) {
1103 	    if (textureIds.size() > 0) {
1104 		id = ((Integer)FreeListManager.
1105 			getObject(FreeListManager.TEXTURE2D)).intValue();
1106 	    } else {
1107 		id = (++textureIdCount);
1108 	    }
1109             return id;
1110 	}
1111     }
1112 
1113     int getTexture3DId() {
1114 	// MasterControl has to handle the ID itself.  2D and 3D ideas must
1115 	// never be the same, so the counter has to be in the MasterControl
1116 	MemoryFreeList textureIds =
1117 	    FreeListManager.getFreeList(FreeListManager.TEXTURE3D);
1118 	synchronized (textureIdLock) {
1119 	    if (textureIds.size > 0) {
1120 		return ((Integer)FreeListManager.
1121 			getObject(FreeListManager.TEXTURE3D)).intValue();
1122 	    }
1123 	    else return (++textureIdCount);
1124 	}
1125     }
1126 
1127     void freeTexture2DId(int id) {
1128 	FreeListManager.freeObject(FreeListManager.TEXTURE2D, new Integer(id));
1129     }
1130 
1131     void freeTexture3DId(int id) {
1132 	FreeListManager.freeObject(FreeListManager.TEXTURE3D, new Integer(id));
1133     }
1134 
1135     int getCanvasId() {
1136         int i;
1137 
1138 	synchronized(canvasIdLock) {
1139 	    // Master control need to keep count itself
1140 	    for(i=canvasFreeIndex; i<canvasIds.length; i++) {
1141 		if(canvasIds[i] == false)
1142 		    break;
1143 	    }
1144 
1145 	    if (i >= canvasIds.length) {
1146                 throw new RuntimeException("Cannot render to more than 32 Canvas3Ds");
1147 	    }
1148 
1149 	    canvasIds[i] = true;
1150 	    canvasFreeIndex = i + 1;
1151 	}
1152 
1153         return i;
1154 
1155     }
1156 
1157     void freeCanvasId(int canvasId) {
1158         // Valid range is [0, 31]
1159 	synchronized(canvasIdLock) {
1160 
1161 	    canvasIds[canvasId] = false;
1162 	    if(canvasFreeIndex > canvasId) {
1163 		canvasFreeIndex = canvasId;
1164 	    }
1165 	}
1166     }
1167 
1168 
1169     /**
1170      * Create a Renderer if it is not already done so.
1171      * This is used for GraphicsConfigTemplate3D passing
1172      * graphics call to RequestRenderer, and for creating
1173      * an off-screen buffer for an off-screen Canvas3D.
1174      */
1175     private Renderer createRenderer(GraphicsConfiguration gc) {
1176 	final GraphicsDevice gd = gc.getDevice();
1177 
1178 	Renderer rdr = (Renderer) Screen3D.deviceRendererMap.get(gd);
1179 	if (rdr != null) {
1180 	    return rdr;
1181 	}
1182 
1183 
1184 	java.security.AccessController.doPrivileged(
1185 	     new java.security.PrivilegedAction() {
1186                     public Object run() {
1187 			Renderer r;
1188 		        synchronized (rootThreadGroup) {
1189 			    r = new Renderer(rootThreadGroup);
1190 			    r.initialize();
1191 			    r.setPriority(threadPriority);
1192 			    Screen3D.deviceRendererMap.put(gd, r);
1193 			}
1194 			return null;
1195 		   }
1196 	});
1197 
1198 	threadListsChanged = true;
1199 
1200 	return (Renderer) Screen3D.deviceRendererMap.get(gd);
1201     }
1202 
1203     /**
1204      * Post the request in queue
1205      */
1206     void postRequest(Integer type, Object obj) {
1207 
1208 	synchronized (mcThreadLock) {
1209 	    synchronized (requestObjList) {
1210 		if (mcThread == null) {
1211 		    if ((type == ACTIVATE_VIEW) ||
1212 			(type == GETBESTCONFIG) ||
1213 			(type == SET_VIEW) ||
1214 			(type == ISCONFIGSUPPORT) ||
1215 			(type == SET_QUERYPROPERTIES) ||
1216 			(type == SET_GRAPHICSCONFIG_FEATURES)) {
1217 			createMasterControlThread();
1218 			requestObjList.add(obj);
1219 			requestTypeList.add(type);
1220 			pendingRequest = true;
1221 		    } else if (type == EMPTY_UNIVERSE) {
1222 			destroyUniverseThreads((VirtualUniverse) obj);
1223 		    } else if (type == STOP_VIEW) {
1224 			View v = (View) obj;
1225 			v.stopViewCount = -1;
1226 			v.isRunning = false;
1227 		    } else if (type == STOP_RENDERER) {
1228 			if (obj instanceof Canvas3D) {
1229 			    ((Canvas3D) obj).isRunningStatus = false;
1230 			} else {
1231 			    ((Renderer) obj).userStop = true;
1232 			}
1233 		    } else if (type == UNREGISTER_VIEW) {
1234 			((View) obj).doneUnregister = true;
1235 		    } else {
1236 			requestObjList.add(obj);
1237 			requestTypeList.add(type);
1238 			pendingRequest = true;
1239 		    }
1240 		} else {
1241 		    requestObjList.add(obj);
1242 		    requestTypeList.add(type);
1243 		    pendingRequest = true;
1244 		}
1245 	    }
1246 	}
1247 
1248 	setWork();
1249     }
1250 
1251 
1252 
1253 
1254     /**
1255      * This procedure is invoked when isRunning is false.
1256      * Return true when there is no more pending request so that
1257      * Thread can terminate. Otherwise we have to recreate
1258      * the MC related threads.
1259      */
1260     boolean mcThreadDone() {
1261 	synchronized (mcThreadLock) {
1262 	    synchronized (requestObjList) {
1263 		if (!pendingRequest) {
1264 		    mcThread = null;
1265 		    if (renderingAttributesStructure.updateThread !=
1266 			null) {
1267 			renderingAttributesStructure.updateThread.finish();
1268 			renderingAttributesStructure.updateThread =
1269 			    null;
1270 		    }
1271 		    renderingAttributesStructure = new RenderingAttributesStructure();
1272 		    if (timerThread != null) {
1273 			timerThread.finish();
1274 			timerThread = null;
1275 		    }
1276 		    if (notificationThread != null) {
1277 			notificationThread.finish();
1278 			notificationThread = null;
1279 		    }
1280 		    requestObjList.clear();
1281 		    requestTypeList.clear();
1282 		    return true;
1283 		}
1284 		running = true;
1285 		createMCThreads();
1286 		return false;
1287 	    }
1288 	}
1289     }
1290 
1291     /**
1292      * Returns whether we are using D3D.
1293      * TODO: most code that cares about this should move into the pipeline
1294      */
1295     final boolean isD3D() {
1296 	return Pipeline.getPipeline().getPipelineType() == Pipeline.Type.NATIVE_D3D;
1297     }
1298 
1299     /**
1300      * Returns whether we are running on Windows
1301      * TODO: most code that cares about this should move into the pipeline
1302      */
1303     static final boolean isWindows() {
1304 	return isWindowsOs;
1305     }
1306 
1307     /**
1308      * Returns a flag indicating whether the sun.jnlp.applet.launcher system
1309      * property is set to true.
1310      */
1311     static boolean isAppletLauncher() {
1312         return appletLauncher;
1313     }
1314 
1315     /**
1316      * This method increments and returns the next time value
1317      * timeLock must get before this procedure is invoked
1318      */
1319     final long getTime() {
1320 	return (time++);
1321     }
1322 
1323 
1324     /**
1325      * This takes a given message and parses it out to the structures and
1326      * marks its time value.
1327      */
1328     void processMessage(J3dMessage message) {
1329 
1330         synchronized (timeLock) {
1331 	    message.time = getTime();
1332 	    sendMessage(message);
1333 	}
1334 	setWork();
1335     }
1336 
1337     /**
1338      * This takes an array of messages and parses them out to the structures and
1339      * marks the time value. Make sure, setWork() is done at the very end
1340      * to make sure all the messages will be processed in the same frame
1341      */
1342     void processMessage(J3dMessage[] messages) {
1343 
1344         synchronized (timeLock) {
1345 	    long time = getTime();
1346 
1347 	    for (int i = 0; i < messages.length; i++) {
1348 		messages[i].time = time;
1349 		sendMessage(messages[i]);
1350 	    }
1351 	}
1352 	setWork();
1353     }
1354 
1355     /**
1356      * This takes the specified notification message and sends it to the
1357      * notification thread for processing.
1358      */
1359     void sendNotification(J3dNotification notification) {
1360         notificationThread.addNotification(notification);
1361     }
1362 
1363     /**
1364      * Create and start the MasterControl Thread.
1365      */
1366     void createMasterControlThread() {
1367         // Issue 364: don't create master control thread if already created
1368         if (mcThread != null) {
1369             return;
1370         }
1371 
1372 	running = true;
1373 	workToDo = true;
1374 	state = RUNNING;
1375 	java.security.AccessController.doPrivileged(
1376 	    new java.security.PrivilegedAction() {
1377                 public Object run() {
1378 		    synchronized (rootThreadGroup) {
1379 			mcThread = new
1380 			    MasterControlThread(rootThreadGroup);
1381 			mcThread.setPriority(threadPriority);
1382 		    }
1383 		    return null;
1384 		}
1385 	});
1386     }
1387 
1388     // assuming the timeLock is already acquired
1389 
1390     /**
1391      * Send a message to another Java 3D thread.
1392      */
1393     void sendMessage(J3dMessage message) {
1394 
1395  	  synchronized (message) {
1396 	    VirtualUniverse u = message.universe;
1397 	    int targetThreads = message.threads;
1398 
1399             if (isCoreLoggable(Level.FINEST)) {
1400                 dumpMessage("sendMessage", message);
1401             }
1402 
1403 	    if ((targetThreads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
1404 		renderingAttributesStructure.addMessage(message);
1405 	    }
1406 
1407 	    // GraphicsContext3D send message with universe = null
1408 	    if (u != null) {
1409 		if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
1410 		    u.geometryStructure.addMessage(message);
1411 		}
1412 		if ((targetThreads & J3dThread.UPDATE_TRANSFORM) != 0) {
1413 		    u.transformStructure.addMessage(message);
1414 		}
1415 		if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
1416 		    u.behaviorStructure.addMessage(message);
1417 		}
1418 		if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
1419 		    u.soundStructure.addMessage(message);
1420 		}
1421 		if ((targetThreads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
1422 		    u.renderingEnvironmentStructure.addMessage(message);
1423 		}
1424 	    }
1425 
1426             if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
1427                 // Note that we don't check for active view
1428                 if (message.view != null && message.view.soundScheduler != null ) {
1429                     // This make sure that message won't lost even
1430                     // though this view not yet register
1431                     message.view.soundScheduler.addMessage(message);
1432                 } else {
1433 		    synchronized (views) {
1434 			View v[] = (View []) views.toArray(false);
1435 			int i = views.arraySize()-1;
1436 			if (u == null) {
1437 			    while (i>=0) {
1438 				v[i--].soundScheduler.addMessage(message);
1439 			    }
1440 			} else {
1441 			    while (i>=0) {
1442 				if (v[i].universe == u) {
1443 				    v[i].soundScheduler.addMessage(message);
1444 				}
1445 				i--;
1446 			    }
1447 			}
1448 		    }
1449                 }
1450             }
1451 
1452 	    if ((targetThreads & J3dThread.UPDATE_RENDER) != 0) {
1453 		// Note that we don't check for active view
1454 		if (message.view != null && message.view.renderBin != null) {
1455 		    // This make sure that message won't lost even
1456 		    // though this view not yet register
1457 		    message.view.renderBin.addMessage(message);
1458 		} else {
1459 		    synchronized (views) {
1460 			View v[] = (View []) views.toArray(false);
1461 			int i = views.arraySize()-1;
1462 			if (u == null) {
1463 			    while (i>=0) {
1464 				v[i--].renderBin.addMessage(message);
1465 			    }
1466 			}
1467 			else {
1468 			    while (i>=0) {
1469 				if (v[i].universe == u) {
1470 				    v[i].renderBin.addMessage(message);
1471 				}
1472 				i--;
1473 			    }
1474 			}
1475 		    }
1476 		}
1477 	    }
1478 
1479 	    if (message.getRefcount() == 0) {
1480 		message.clear();
1481 	    }
1482 	  }
1483     }
1484 
1485 
1486     /**
1487      * Send a message to another Java 3D thread.
1488      * This variant is only call by TimerThread for Input Device Scheduler
1489      * or to redraw all View for RenderThread
1490      */
1491     void sendRunMessage(int targetThreads) {
1492 
1493 	synchronized (timeLock) {
1494 
1495 	    long time = getTime();
1496 
1497 	    if ((targetThreads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
1498 		synchronized (inputDeviceThreads) {
1499 		    InputDeviceScheduler ds[] = (InputDeviceScheduler [])
1500 			inputDeviceThreads.toArray(false);
1501 		    for (int i=inputDeviceThreads.size()-1; i >=0; i--) {
1502 			if (ds[i].physicalEnv.activeViewRef > 0) {
1503 			    ds[i].getThreadData().lastUpdateTime =
1504 				time;
1505 			}
1506 		    }
1507 
1508 		    // timerThread instance in MC will set to null in
1509 		    // destroyUniverseThreads() so we need to check if
1510 		    // TimerThread kick in to sendRunMessage() after that.
1511 		    // It happens because TimerThread is the only thread run
1512 		    // asychronizously with MasterControl thread.
1513 
1514 		    if (timerThread != null) {
1515 			// Notify TimerThread to wakeup this procedure
1516 			// again next time.
1517 			timerThread.addInputDeviceSchedCond();
1518 		    }
1519 		}
1520 	    }
1521 	    if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
1522 		synchronized (renderThreadData) {
1523 		    J3dThreadData[] threads = (J3dThreadData [])
1524 			renderThreadData.toArray(false);
1525 		    int i=renderThreadData.arraySize()-1;
1526 		    J3dThreadData thr;
1527 		    while (i>=0) {
1528 			thr = threads[i--];
1529 			if ( thr.view.renderBinReady) {
1530 			    thr.lastUpdateTime = time;
1531 			}
1532 		    }
1533 		}
1534 	    }
1535 	}
1536 	setWork();
1537     }
1538 
1539     /**
1540      * Send a message to another Java 3D thread.
1541      * This variant is only call by TimerThread for Sound Scheduler
1542      */
1543     void sendRunMessage(long waitTime, View view, int targetThreads) {
1544 
1545 	synchronized (timeLock) {
1546 
1547 	    long time = getTime();
1548 
1549 	    if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
1550 		if (view.soundScheduler != null)  {
1551 		    view.soundScheduler.threadData.lastUpdateTime = time;
1552 		}
1553 		// wakeup this procedure next time
1554 		// QUESTION: waitTime calculated some milliseconds BEFORE
1555 		//     this methods getTime() called - shouldn't actual
1556 		//     sound Complete time be passed by SoundScheduler
1557 		// QUESTION: will this wake up only soundScheduler associated
1558 		//     with this view?? (since only it's lastUpdateTime is set)
1559 		//     or all soundSchedulers??
1560 		timerThread.addSoundSchedCond(time+waitTime);
1561 	    }
1562 	}
1563 	setWork();
1564     }
1565 
1566     /**
1567      * Send a message to another Java 3D thread.
1568      * This variant is only called to update Render Thread
1569      */
1570     void sendRunMessage(View v, int targetThreads) {
1571 
1572 	synchronized (timeLock) {
1573 	    long time = getTime();
1574 
1575 	    if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
1576 		synchronized (renderThreadData) {
1577 		    J3dThreadData[] threads = (J3dThreadData [])
1578 			renderThreadData.toArray(false);
1579 		    int i=renderThreadData.arraySize()-1;
1580 		    J3dThreadData thr;
1581 		    while (i>=0) {
1582 			thr = threads[i--];
1583 			if (thr.view == v && v.renderBinReady) {
1584 			    thr.lastUpdateTime = time;
1585 			}
1586 		    }
1587 		}
1588 	    }
1589 	}
1590 	setWork();
1591     }
1592 
1593 
1594     /**
1595      * This sends a run message to the given threads.
1596      */
1597     void sendRunMessage(VirtualUniverse u, int targetThreads) {
1598 	// We don't sendRunMessage to update structure except Behavior
1599 
1600 	synchronized (timeLock) {
1601 	    long time = getTime();
1602 
1603 	    if ((targetThreads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
1604 		if (u.behaviorScheduler != null) {
1605 		    u.behaviorScheduler.getThreadData(null,
1606 						      null).lastUpdateTime = time;
1607 		}
1608 	    }
1609 
1610 	    if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
1611 		u.behaviorStructure.threadData.lastUpdateTime = time;
1612 	    }
1613 
1614 	    if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
1615 		u.geometryStructure.threadData.lastUpdateTime = time;
1616 	    }
1617 
1618 	    if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
1619 		u.soundStructure.threadData.lastUpdateTime = time;
1620 	    }
1621 
1622 	    if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
1623 		synchronized (views) {
1624 		    View v[] = (View []) views.toArray(false);
1625 		    for (int i= views.arraySize()-1; i >=0; i--) {
1626 			if ((v[i].soundScheduler != null) &&
1627 			    (v[i].universe == u)) {
1628 			    v[i].soundScheduler.threadData.lastUpdateTime = time;
1629 			}
1630 		    }
1631 		}
1632 	    }
1633 
1634 	    if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
1635 
1636 		synchronized (renderThreadData) {
1637 		    J3dThreadData[] threads = (J3dThreadData [])
1638 			renderThreadData.toArray(false);
1639 		    int i=renderThreadData.arraySize()-1;
1640 		    J3dThreadData thr;
1641 		    while (i>=0) {
1642 			thr = threads[i--];
1643 			if (thr.view.universe == u && thr.view.renderBinReady) {
1644 			    thr.lastUpdateTime = time;
1645 			}
1646 		    }
1647 		}
1648 	    }
1649 	}
1650 
1651 	setWork();
1652     }
1653 
1654 
1655     /**
1656      * Return a clone of View, we can't access
1657      * individual element of View after getting the size
1658      * in separate API call without synchronized views.
1659      */
1660     UnorderList cloneView() {
1661 	return (UnorderList) views.clone();
1662     }
1663 
1664     /**
1665      * Return true if view is already registered with MC
1666      */
1667     boolean isRegistered(View view) {
1668 	return views.contains(view);
1669     }
1670 
1671     /**
1672      * This snapshots the time values to be used for this iteration.
1673      * Note that this method is called without the timeLock held.
1674      * We must synchronize on timeLock to prevent updating
1675      * thread.lastUpdateTime from user thread in sendMessage()
1676      * or sendRunMessage().
1677      */
1678     private void updateTimeValues() {
1679         synchronized (timeLock) {
1680             int i=0;
1681             J3dThreadData lastThread=null;
1682             J3dThreadData thread=null;
1683             long lastTime = currentTime;
1684 
1685             currentTime = getTime();
1686 
1687             J3dThreadData threads[] = (J3dThreadData [])
1688                                     stateWorkThreads.toArray(false);
1689             int size = stateWorkThreads.arraySize();
1690 
1691             while (i<lastTransformStructureThread) {
1692                 thread = threads[i++];
1693 
1694                 if ((thread.lastUpdateTime > thread.lastRunTime) &&
1695                     !thread.thread.userStop) {
1696                     lastThread = thread;
1697                     thread.needsRun = true;
1698                     thread.threadOpts = J3dThreadData.CONT_THREAD;
1699                     thread.lastRunTime =  currentTime;
1700                 } else {
1701                     thread.needsRun = false;
1702                 }
1703             }
1704 
1705             if (lastThread != null) {
1706                 lastThread.threadOpts =  J3dThreadData.WAIT_ALL_THREADS;
1707                 lastThread = null;
1708             }
1709 
1710             while (i<lastStructureUpdateThread) {
1711                 thread = threads[i++];
1712                 if ((thread.lastUpdateTime > thread.lastRunTime) &&
1713                     !thread.thread.userStop) {
1714                     lastThread = thread;
1715                     thread.needsRun = true;
1716                     thread.threadOpts = J3dThreadData.CONT_THREAD;
1717                     thread.lastRunTime = currentTime;
1718                 } else {
1719                     thread.needsRun = false;
1720                 }
1721             }
1722             if (lastThread != null) {
1723                 lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
1724                 lastThread = null;
1725             }
1726 
1727             while (i<size) {
1728                 thread = threads[i++];
1729                 if ((thread.lastUpdateTime > thread.lastRunTime) &&
1730                     !thread.thread.userStop) {
1731                     lastThread = thread;
1732                     thread.needsRun = true;
1733                     thread.threadOpts = J3dThreadData.CONT_THREAD;
1734                     thread.lastRunTime = currentTime;
1735                 } else {
1736                     thread.needsRun = false;
1737                 }
1738             }
1739             if (lastThread != null) {
1740                 lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
1741                 lastThread = null;
1742             }
1743 
1744 
1745             threads = (J3dThreadData []) renderWorkThreads.toArray(false);
1746             size = renderWorkThreads.arraySize();
1747             View v;
1748             J3dThreadData lastRunThread = null;
1749             waitTimestamp++;
1750             sleepTime = 0L;
1751 
1752             boolean threadToRun = false; // Not currently used
1753 
1754             // Fix for Issue 12: loop through the list of threads, calling
1755             // computeCycleTime() exactly once per view. This ensures that
1756             // all threads for a given view see consistent values for
1757             // isMinCycleTimeAchieve and sleepTime.
1758             v = null;
1759             for (i=0; i<size; i++) {
1760                 thread = threads[i];
1761                 if (thread.view != v) {
1762                     thread.view.computeCycleTime();
1763                     // Set sleepTime to the value needed to satify the
1764                     // minimum cycle time of the slowest view
1765                     if (thread.view.sleepTime > sleepTime) {
1766                         sleepTime = thread.view.sleepTime;
1767                     }
1768                 }
1769                 v = thread.view;
1770             }
1771 
1772             v = null;
1773             for (i=0; i<size; i++) {
1774                 thread = threads[i];
1775                 if (thread.canvas == null) { // Only for swap thread
1776                     ((Object []) thread.threadArgs)[3] = null;
1777                 }
1778                 if ((thread.lastUpdateTime > thread.lastRunTime) &&
1779                     !thread.thread.userStop) {
1780 
1781                     if (thread.thread.lastWaitTimestamp == waitTimestamp) {
1782                         // This renderer thread is repeated. We must wait
1783                         // until all previous renderer threads done before
1784                         // allowing this thread to continue. Note that
1785                         // lastRunThread can't be null in this case.
1786                         waitTimestamp++;
1787                         if (thread.view != v) {
1788                             // A new View is start
1789                             v = thread.view;
1790                             threadToRun = true;
1791                             lastRunThread.threadOpts =
1792                                 (J3dThreadData.STOP_TIMER |
1793                                  J3dThreadData.WAIT_ALL_THREADS);
1794                             ((Object []) lastRunThread.threadArgs)[3] = lastRunThread.view;
1795                             thread.threadOpts = (J3dThreadData.START_TIMER |
1796                                                  J3dThreadData.CONT_THREAD);
1797                         } else {
1798                             if ((lastRunThread.threadOpts &
1799                                  J3dThreadData.START_TIMER) != 0) {
1800                                 lastRunThread.threadOpts =
1801                                     (J3dThreadData.START_TIMER |
1802                                      J3dThreadData.WAIT_ALL_THREADS);
1803 
1804                             } else {
1805                                 lastRunThread.threadOpts =
1806                                     J3dThreadData.WAIT_ALL_THREADS;
1807                             }
1808                             thread.threadOpts = J3dThreadData.CONT_THREAD;
1809 
1810                         }
1811                     } else {
1812                         if (thread.view != v) {
1813                             v = thread.view;
1814                             threadToRun = true;
1815                             // Although the renderer thread is not
1816                             // repeated. We still need to wait all
1817                             // previous renderer threads if new View
1818                             // start.
1819                             if (lastRunThread != null) {
1820                                 lastRunThread.threadOpts =
1821                                     (J3dThreadData.STOP_TIMER |
1822                                      J3dThreadData.WAIT_ALL_THREADS);
1823                                 ((Object []) lastRunThread.threadArgs)[3]
1824                                     = lastRunThread.view;
1825                             }
1826                             thread.threadOpts = (J3dThreadData.START_TIMER |
1827                                                  J3dThreadData.CONT_THREAD);
1828                         } else {
1829                             thread.threadOpts = J3dThreadData.CONT_THREAD;
1830                         }
1831                     }
1832                     thread.thread.lastWaitTimestamp = waitTimestamp;
1833                     thread.needsRun = true;
1834                     thread.lastRunTime = currentTime;
1835                     lastRunThread = thread;
1836                 } else {
1837                     thread.needsRun = false;
1838                 }
1839             }
1840 
1841 
1842             if (lastRunThread != null) {
1843                 lastRunThread.threadOpts =
1844                                     (J3dThreadData.STOP_TIMER |
1845                                      J3dThreadData.WAIT_ALL_THREADS|
1846                                      J3dThreadData.LAST_STOP_TIMER);
1847                 lockGeometry = true;
1848                 ((Object []) lastRunThread.threadArgs)[3] = lastRunThread.view;
1849             }  else {
1850                 lockGeometry = false;
1851             }
1852         }
1853 
1854         // Issue 275 - go to sleep without holding timeLock
1855 	// Sleep for the amount of time needed to satisfy the minimum
1856 	// cycle time for all views.
1857 	if (sleepTime > 0) {
1858 	    // System.err.println("MasterControl: sleep(" + sleepTime + ")");
1859 	    try {
1860 		Thread.sleep(sleepTime);
1861 	    } catch (InterruptedException e) {
1862 		System.err.println(e);
1863 	    }
1864 	    // System.err.println("MasterControl: done sleeping");
1865 	}
1866     }
1867 
1868     private void createUpdateThread(J3dStructure structure) {
1869 	final J3dStructure s = structure;
1870 
1871 	if (s.updateThread == null) {
1872 	    java.security.AccessController.doPrivileged(
1873 	        new java.security.PrivilegedAction() {
1874                    public Object run() {
1875 		       synchronized (rootThreadGroup) {
1876                            s.updateThread = new StructureUpdateThread(
1877                    	        rootThreadGroup, s, s.threadType);
1878 			   s.updateThread.setPriority(threadPriority);
1879 		       }
1880 		       return null;
1881 		   }
1882 	    });
1883 	    s.updateThread.initialize();
1884 	    s.threadData.thread = s.updateThread;
1885 	    // This takes into accout for thread that just destroy and
1886 	    // create again. In this case the threadData may receive
1887 	    // message before the thread actually created. We don't want
1888 	    // the currentTime to overwrite the update time of which
1889 	    // is set by threadData when get message.
1890 	    s.threadData.lastUpdateTime = Math.max(currentTime,
1891 						   s.threadData.lastUpdateTime);
1892 	}
1893     }
1894 
1895     private void emptyMessageList(J3dStructure structure, View v) {
1896 	if (structure != null) {
1897 	    if (v == null) {
1898 		if (structure.threadData != null) {
1899 		    structure.threadData.thread = null;
1900 		}
1901 
1902 		if (structure.updateThread != null) {
1903 		    structure.updateThread.structure = null;
1904 		}
1905 		structure.updateThread = null;
1906 	    }
1907 	    boolean otherViewExist = false;
1908 	    if ((v != null) && (v.universe != null)) {
1909 		// Check if there is any other View register with the
1910 		// same universe
1911 		for (int i=views.size()-1; i >= 0; i--) {
1912 		    if (((View) views.get(i)).universe == v.universe) {
1913 			otherViewExist = true;
1914 			break;
1915 		    }
1916 		}
1917 	    }
1918 
1919 
1920 	    UnorderList mlist = structure.messageList;
1921 	    // Note that message is add at the end of array
1922 	    synchronized (mlist) {
1923 		int size = mlist.size();
1924 		if (size > 0) {
1925 		    J3dMessage mess[] = (J3dMessage []) mlist.toArray(false);
1926 		    J3dMessage m;
1927 		    int i = 0;
1928 
1929 		    Object oldRef= null;
1930 		    while (i < size) {
1931 			m = mess[i];
1932 			if ((v == null) || (m.view == v) ||
1933 			    ((m.view == null) && !otherViewExist)) {
1934 			    if (m.type == J3dMessage.INSERT_NODES) {
1935 				// There is another View register request
1936 				// immediately following, so no need
1937 				// to remove message.
1938 				break;
1939 			    }
1940 			    // Some other thread may still using this
1941 			    // message so we should not directly
1942 			    // add this message to free lists
1943 			    m.decRefcount();
1944 			    mlist.removeOrdered(i);
1945 			    size--;
1946 			} else {
1947 			    i++;
1948 			}
1949 		    }
1950 		}
1951 	    }
1952 	}
1953     }
1954 
1955     private void destroyUpdateThread(J3dStructure structure) {
1956 	// If unregisterView message got before EMPTY_UNIVERSE
1957 	// message, then updateThread is already set to null.
1958 	if (structure.updateThread != null) {
1959 	    structure.updateThread.finish();
1960 	    structure.updateThread.structure = null;
1961 	    structure.updateThread = null;
1962 	}
1963 	structure.threadData.thread = null;
1964 	structure.clearMessages();
1965     }
1966 
1967     /**
1968      * This register a View with MasterControl.
1969      * The View has at least one Canvas3D added to a container.
1970      */
1971     private void registerView(View v) {
1972 	final VirtualUniverse univ = v.universe;
1973 
1974 	if (views.contains(v) && regUniverseList.contains(univ)) {
1975 	    return;  // already register
1976 	}
1977 
1978 	if (timerThread == null) {
1979 	    // This handle the case when MC shutdown and restart in
1980 	    // a series of pending request
1981 	    running = true;
1982 	    createMCThreads();
1983 	}
1984 	// If viewId is null, assign one ..
1985 	v.assignViewId();
1986 
1987 	// Create thread if not done before
1988 	createUpdateThread(univ.behaviorStructure);
1989 	createUpdateThread(univ.geometryStructure);
1990 	createUpdateThread(univ.soundStructure);
1991 	createUpdateThread(univ.renderingEnvironmentStructure);
1992 	createUpdateThread(univ.transformStructure);
1993 
1994 	// create Behavior scheduler
1995 	J3dThreadData threadData = null;
1996 
1997 	if (univ.behaviorScheduler == null) {
1998 	    java.security.AccessController.doPrivileged(
1999 		new java.security.PrivilegedAction() {
2000                        public Object run() {
2001 			   synchronized (rootThreadGroup) {
2002 			       univ.behaviorScheduler = new BehaviorScheduler(
2003 						      rootThreadGroup, univ);
2004 			       univ.behaviorScheduler.setPriority(threadPriority);
2005 			   }
2006 			   return null;
2007 		       }
2008 	    });
2009 	    univ.behaviorScheduler.initialize();
2010 	    univ.behaviorScheduler.userStop = v.stopBehavior;
2011 	    threadData = univ.behaviorScheduler.getThreadData(null, null);
2012 	    threadData.thread = univ.behaviorScheduler;
2013 	    threadData.threadType = J3dThread.BEHAVIOR_SCHEDULER;
2014 	    threadData.lastUpdateTime = Math.max(currentTime,
2015 						 threadData.lastUpdateTime);
2016 	}
2017 
2018 	createUpdateThread(v.renderBin);
2019 	createUpdateThread(v.soundScheduler);
2020 
2021 	if (v.physicalEnvironment != null) {
2022 	    v.physicalEnvironment.addUser(v);
2023 	}
2024 	// create InputDeviceScheduler
2025 	evaluatePhysicalEnv(v);
2026 
2027 	regUniverseList.addUnique(univ);
2028 	views.addUnique(v);
2029     }
2030 
2031 
2032 
2033     /**
2034      * This unregister a View with MasterControl.
2035      * The View no longer has any Canvas3Ds in a container.
2036      */
2037     private void unregisterView(View v) {
2038 
2039 	if (!views.remove(v)) {
2040 	    v.active = false;
2041 	    v.doneUnregister = true;
2042 	    return; // already unregister
2043 	}
2044 
2045 	if (v.active) {
2046 	    viewDeactivate(v);
2047 	}
2048 
2049 	if(J3dDebug.devPhase) {
2050 	    J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
2051 			     "MC: Destroy Sound Scheduler and RenderBin Update thread");
2052 	}
2053 
2054 	v.soundScheduler.updateThread.finish();
2055 	v.renderBin.updateThread.finish();
2056 	v.soundScheduler.updateThread = null;
2057 	v.renderBin.updateThread = null;
2058 
2059 	// remove VirtualUniverse related threads if Universe
2060 	// is empty
2061         VirtualUniverse univ = v.universe;
2062 	int i;
2063 
2064 	synchronized (timeLock) {
2065 	    // The reason we need to sync. with timeLock is because we
2066 	    // don't want user thread running sendMessage() to
2067 	    // dispatch it in different structure queue when
2068 	    // part of the structure list is empty at the same time.
2069 	    // This will cause inconsistence in the message reference
2070 	    // count.
2071 	    emptyMessageList(v.soundScheduler, v);
2072 	    emptyMessageList(v.renderBin, v);
2073 
2074 	    if (univ.isEmpty()) {
2075 		destroyUniverseThreads(univ);
2076 	    }  else {
2077 		emptyMessageList(univ.behaviorStructure, v);
2078 		emptyMessageList(univ.geometryStructure, v);
2079 		emptyMessageList(univ.soundStructure, v);
2080 		emptyMessageList(univ.renderingEnvironmentStructure, v);
2081 		emptyMessageList(univ.transformStructure, v);
2082 	    }
2083 	}
2084 
2085 	if (v.physicalEnvironment != null) {
2086 	    v.physicalEnvironment.removeUser(v);
2087 	}
2088 
2089 	// remove all InputDeviceScheduler if this is the last View
2090 	UnorderList list = new UnorderList(1, PhysicalEnvironment.class);
2091 	for (Enumeration e = PhysicalEnvironment.physicalEnvMap.keys();
2092 	     e.hasMoreElements(); ) {
2093 	    PhysicalEnvironment phyEnv = (PhysicalEnvironment) e.nextElement();
2094 	    InputDeviceScheduler sched = (InputDeviceScheduler)
2095 		PhysicalEnvironment.physicalEnvMap.get(phyEnv);
2096 	    for (i=phyEnv.users.size()-1; i>=0; i--) {
2097 		if (views.contains((View) phyEnv.users.get(i))) {
2098 		    // at least one register view refer to it.
2099 		    break;
2100 		}
2101 	    }
2102 	    if (i < 0) {
2103 		if(J3dDebug.devPhase) {
2104 		    J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
2105 				     "MC: Destroy InputDeviceScheduler thread "
2106 				     + sched);
2107 		}
2108 		sched.finish();
2109 		phyEnv.inputsched = null;
2110 		list.add(phyEnv);
2111 	    }
2112 	}
2113 	for (i=list.size()-1; i>=0; i--) {
2114 	    PhysicalEnvironment.physicalEnvMap.remove(list.get(i));
2115 	}
2116 
2117 
2118 	freeContext(v);
2119 
2120 	if (views.isEmpty()) {
2121 	    if(J3dDebug.devPhase) {
2122 		J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
2123 				 "MC: Destroy all Renderers");
2124 	    }
2125 	    // remove all Renderers if this is the last View
2126 	    for (Enumeration e = Screen3D.deviceRendererMap.elements();
2127 		 e.hasMoreElements(); ) {
2128 		Renderer rdr = (Renderer) e.nextElement();
2129 		Screen3D scr;
2130 
2131 		rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
2132 		runMonitor(RUN_RENDERER_CLEANUP, null, null, null, rdr);
2133 		scr = rdr.onScreen;
2134 		if (scr != null) {
2135 		    if (scr.renderer != null) {
2136 			rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
2137 			runMonitor(RUN_RENDERER_CLEANUP, null, null,
2138 				   null, scr.renderer);
2139 			scr.renderer = null;
2140 		    }
2141 
2142 		}
2143 		scr = rdr.offScreen;
2144 		if (scr != null) {
2145 		    if (scr.renderer != null) {
2146 			rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
2147 			runMonitor(RUN_RENDERER_CLEANUP, null, null,
2148 				   null, scr.renderer);
2149 			scr.renderer = null;
2150 		    }
2151 		}
2152 		rdr.onScreen = null;
2153 		rdr.offScreen = null;
2154 	    }
2155 
2156 	    // cleanup ThreadData corresponds to the view in renderer
2157 	    for (Enumeration e = Screen3D.deviceRendererMap.elements();
2158 		 e.hasMoreElements(); ) {
2159 		Renderer rdr = (Renderer) e.nextElement();
2160 		rdr.cleanup();
2161 	    }
2162 	    // We have to reuse renderer even though MC exit
2163 	    // see bug 4363279
2164 	    //  Screen3D.deviceRendererMap.clear();
2165 
2166 	} else {
2167 	    // cleanup ThreadData corresponds to the view in renderer
2168 	    for (Enumeration e = Screen3D.deviceRendererMap.elements();
2169 		 e.hasMoreElements(); ) {
2170 		Renderer rdr = (Renderer) e.nextElement();
2171 		rdr.cleanupView();
2172 	    }
2173 	}
2174 
2175 
2176 	freeMessageList.add(univ);
2177 	freeMessageList.add(v);
2178 
2179 	evaluateAllCanvases();
2180 	stateWorkThreads.clear();
2181 	renderWorkThreads.clear();
2182 	requestRenderWorkThreads.clear();
2183 	threadListsChanged = true;
2184 
2185 	// This notify VirtualUniverse waitForMC() thread to continue
2186 	v.doneUnregister = true;
2187     }
2188 
2189 
2190     /**
2191      * This procedure create MC thread that start together with MC.
2192      */
2193     void createMCThreads() {
2194 
2195 	// There is only one renderingAttributesUpdate Thread globally
2196 	createUpdateThread(renderingAttributesStructure);
2197 
2198 	// Create timer thread
2199 	java.security.AccessController.doPrivileged(
2200 			    new java.security.PrivilegedAction() {
2201               public Object run() {
2202 		  synchronized (rootThreadGroup) {
2203 		      timerThread = new TimerThread(rootThreadGroup);
2204 		      timerThread.setPriority(threadPriority);
2205 		  }
2206 		  return null;
2207 	      }
2208 	});
2209 	timerThread.start();
2210 
2211         // Create notification thread
2212 	java.security.AccessController.doPrivileged(
2213 			    new java.security.PrivilegedAction() {
2214               public Object run() {
2215 		  synchronized (rootThreadGroup) {
2216 		      notificationThread = new NotificationThread(rootThreadGroup);
2217 		      notificationThread.setPriority(threadPriority);
2218 		  }
2219 		  return null;
2220 	      }
2221 	});
2222 	notificationThread.start();
2223     }
2224 
2225     /**
2226      * Destroy all VirtualUniverse related threads.
2227      * This procedure may call two times when Locale detach in a
2228      * live viewPlatform.
2229      */
2230     private void destroyUniverseThreads(VirtualUniverse univ) {
2231 
2232 	if (regUniverseList.contains(univ)) {
2233 	    if (J3dDebug.devPhase) {
2234 		J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
2235 				 "MC: Destroy universe threads " + univ);
2236 	    }
2237 	    destroyUpdateThread(univ.behaviorStructure);
2238 	    destroyUpdateThread(univ.geometryStructure);
2239 	    destroyUpdateThread(univ.soundStructure);
2240 	    destroyUpdateThread(univ.renderingEnvironmentStructure);
2241 	    destroyUpdateThread(univ.transformStructure);
2242 	    univ.behaviorScheduler.finish();
2243 	    univ.behaviorScheduler.free();
2244 	    univ.behaviorScheduler = null;
2245 	    univ.initMCStructure();
2246 	    activeUniverseList.remove(univ);
2247 	    regUniverseList.remove(univ);
2248 	} else {
2249 	    emptyMessageList(univ.behaviorStructure, null);
2250 	    emptyMessageList(univ.geometryStructure, null);
2251 	    emptyMessageList(univ.soundStructure, null);
2252 	    emptyMessageList(univ.renderingEnvironmentStructure, null);
2253 	    emptyMessageList(univ.transformStructure, null);
2254 	}
2255 
2256 	if (regUniverseList.isEmpty() && views.isEmpty()) {
2257 	    if(J3dDebug.devPhase) {
2258 		J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
2259 				 "MC: Destroy RenderingAttributes Update and Timer threads");
2260 	    }
2261 	    if (renderingAttributesStructure.updateThread != null) {
2262 		renderingAttributesStructure.updateThread.finish();
2263 		renderingAttributesStructure.updateThread = null;
2264 	    }
2265 	    renderingAttributesStructure.messageList.clear();
2266 	    renderingAttributesStructure.objList = new ArrayList();
2267 	    renderingAttributesStructure = new RenderingAttributesStructure();
2268 	    if (timerThread != null) {
2269 		timerThread.finish();
2270 		timerThread = null;
2271 	    }
2272             if (notificationThread != null) {
2273                 notificationThread.finish();
2274                 notificationThread = null;
2275             }
2276 
2277 	    // shouldn't all of these be synchronized ???
2278 	    synchronized (VirtualUniverse.mc.deviceScreenMap) {
2279 		deviceScreenMap.clear();
2280 	    }
2281 
2282 	    mirrorObjects.clear();
2283 	    // Note: We should not clear the DISPLAYLIST/TEXTURE
2284 	    // list here because other structure may release them
2285 	    // later
2286 
2287 	    for(int i=0; i<canvasIds.length; i++) {
2288 		canvasIds[i] = false;
2289 	    }
2290 	    canvasFreeIndex = 0;
2291 
2292 	    renderOnceList.clear();
2293 	    timestampUpdateList.clear();
2294 
2295 	    defaultRenderMethod = null;
2296 	    text3DRenderMethod = null;
2297 	    vertexArrayRenderMethod = null;
2298 	    displayListRenderMethod = null;
2299 	    compressedGeometryRenderMethod = null;
2300 	    orientedShape3DRenderMethod = null;
2301 	    // Terminate MC thread
2302 	    running = false;
2303 	}
2304     }
2305 
2306     /**
2307      * Note that we have to go through all views instead of
2308      * evaluate only the canvas in a single view since each screen
2309      * may share by multiple view
2310      */
2311     private void evaluateAllCanvases() {
2312 
2313 	synchronized (renderThreadData) {
2314 	    // synchronized to prevent lost message when
2315 	    // renderThreadData is clear
2316 
2317 	    // First remove all renderrenderThreadData
2318 	    renderThreadData.clear();
2319 
2320 	    // Second reset canvasCount to zero
2321 	    View viewArr[] = (View []) views.toArray(false);
2322 	    for (int i=views.size()-1; i>=0; i--) {
2323 		viewArr[i].getCanvasList(true); // force canvas cache update
2324 		Screen3D screens[] = viewArr[i].getScreens();
2325 		for (int j=screens.length-1; j>=0; j--) {
2326 		    screens[j].canvasCount = 0;
2327 		}
2328 	    }
2329 
2330 
2331 	    // Third create render thread and message thread
2332 	    for (int i=views.size()-1; i>=0; i--) {
2333 		View v = viewArr[i];
2334 		Canvas3D canvasList[][] = v.getCanvasList(false);
2335 		if (!v.active) {
2336 		    continue;
2337 		}
2338 
2339 		for (int j=canvasList.length-1; j>=0; j--) {
2340 		    boolean added = false;
2341 
2342 		    for (int k=canvasList[j].length-1; k>=0; k--) {
2343 			Canvas3D cv = canvasList[j][k];
2344 
2345 			final Screen3D screen  = cv.screen;
2346 
2347 			if (cv.active) {
2348 			    if (screen.canvasCount++ == 0) {
2349 				// Create Renderer, one per screen
2350 				if (screen.renderer == null) {
2351                     	        // get the renderer created for the graphics
2352 			        // device of the screen of the canvas
2353 				// No need to synchronized since only
2354 				// MC use it.
2355 				    Renderer rdr =
2356 					(Renderer) screen.deviceRendererMap.get(
2357 							cv.screen.graphicsDevice);
2358 				    if (rdr == null) {
2359 					java.security.AccessController.doPrivileged(
2360 					    new java.security.PrivilegedAction() {
2361                                               public Object run() {
2362 
2363 						  synchronized (rootThreadGroup) {
2364 						      screen.renderer
2365 							  = new Renderer(
2366  							      rootThreadGroup);
2367 						      screen.renderer.setPriority(threadPriority);
2368 						  }
2369                                                  return null;
2370                                           }
2371 					});
2372 					screen.renderer.initialize();
2373 					screen.deviceRendererMap.put(
2374 					     screen.graphicsDevice, screen.renderer);
2375 				    } else {
2376 					screen.renderer = rdr;
2377 				    }
2378 				}
2379 			    }
2380 		    	    // offScreen canvases will be handled by the
2381 			    // request renderer, so don't add offScreen canvas
2382 			    // the render list
2383                             //
2384                             // Issue 131: Automatic offscreen canvases need to
2385                             // be added to onscreen list. Special case.
2386                             //
2387                             // TODO KCR Issue 131: this should probably be
2388                             // changed to a list of screens since multiple
2389                             // off-screen canvases (either auto or manual) can
2390                             // be used by the same renderer
2391 		            if (!cv.manualRendering) {
2392 				screen.renderer.onScreen = screen;
2393 			    } else {
2394 				screen.renderer.offScreen = screen;
2395 				continue;
2396 			    }
2397 
2398 			    if (!added) {
2399 				// Swap message data thread, one per
2400 				// screen only. Note that we don't set
2401 				// lastUpdateTime for this thread so
2402 				// that it won't run in the first round
2403 			        J3dThreadData renderData =
2404 			      	    screen.renderer.getThreadData(v, null);
2405 				renderThreadData.add(renderData);
2406 
2407 				// only if renderBin is ready then we
2408 				// update the lastUpdateTime to make it run
2409 				if (v.renderBinReady) {
2410 	    		    	    renderData.lastUpdateTime =
2411 					Math.max(currentTime,
2412 						   renderData.lastUpdateTime);
2413 				}
2414 				added = true;
2415 			    }
2416 			    // Renderer message data thread
2417 			    J3dThreadData renderData =
2418 			      	    screen.renderer.getThreadData(v, cv);
2419 			    renderThreadData.add(renderData);
2420 			    if (v.renderBinReady) {
2421 	    		    	renderData.lastUpdateTime =
2422 					Math.max(currentTime,
2423 						   renderData.lastUpdateTime);
2424 			    }
2425 			}
2426 		    }
2427 		}
2428 
2429 	    }
2430 	}
2431 
2432 	threadListsChanged = true;
2433     }
2434 
2435     private void evaluatePhysicalEnv(View v) {
2436 	final PhysicalEnvironment env = v.physicalEnvironment;
2437 
2438 	if (env.inputsched == null) {
2439 	    java.security.AccessController.doPrivileged(
2440 		 new java.security.PrivilegedAction() {
2441                       public Object run() {
2442 			  synchronized (rootThreadGroup) {
2443 			      env.inputsched = new InputDeviceScheduler(
2444 							rootThreadGroup,
2445 							env);
2446 			      env.inputsched.setPriority(threadPriority);
2447 			  }
2448 			  return null;
2449 		      }
2450 	    });
2451 	    env.inputsched.start();
2452 	    PhysicalEnvironment.physicalEnvMap.put(env, env.inputsched);
2453 	}
2454 	threadListsChanged = true;
2455     }
2456 
2457     final private void addToStateThreads(J3dThreadData threadData) {
2458 	if (threadData.thread.active) {
2459 	    stateWorkThreads.add(threadData);
2460 	}
2461     }
2462 
2463 
2464     private void assignNewPrimaryView(VirtualUniverse univ) {
2465 
2466 	View currentPrimary = univ.getCurrentView();
2467 
2468 	if (currentPrimary != null) {
2469 	    currentPrimary.primaryView = false;
2470 	}
2471 
2472 	View v[] = (View []) views.toArray(false);
2473 	int nviews = views.size();
2474 	for (int i=0; i<nviews; i++) {
2475 	    View view = v[i];
2476 	    if (view.active && view.isRunning &&
2477 		(univ == view.universe)) {
2478 		view.primaryView = true;
2479 		univ.setCurrentView(view);
2480 		return;
2481 	    }
2482 	}
2483 	univ.setCurrentView(null);
2484     }
2485 
2486 
2487     /**
2488      * This returns the default RenderMethod
2489      */
2490     RenderMethod getDefaultRenderMethod() {
2491 	if (defaultRenderMethod == null) {
2492 	    defaultRenderMethod = new DefaultRenderMethod();
2493 	}
2494 	return defaultRenderMethod;
2495     }
2496 
2497     /**
2498      * This returns the text3d RenderMethod
2499      */
2500     RenderMethod getText3DRenderMethod() {
2501 	if (text3DRenderMethod == null) {
2502 	    text3DRenderMethod = new Text3DRenderMethod();
2503 	}
2504 	return text3DRenderMethod;
2505     }
2506 
2507 
2508     /**
2509      * This returns the vertexArray RenderMethod
2510      */
2511     RenderMethod getVertexArrayRenderMethod() {
2512 	if (vertexArrayRenderMethod == null) {
2513 	    vertexArrayRenderMethod = new VertexArrayRenderMethod();
2514 	}
2515 	return vertexArrayRenderMethod;
2516     }
2517 
2518     /**
2519      * This returns the displayList RenderMethod
2520      */
2521     RenderMethod getDisplayListRenderMethod() {
2522         if (displayListRenderMethod == null) {
2523             displayListRenderMethod = new DisplayListRenderMethod();
2524         }
2525         return displayListRenderMethod;
2526     }
2527 
2528     /**
2529      * This returns the compressed geometry RenderMethod
2530      */
2531     RenderMethod getCompressedGeometryRenderMethod() {
2532         if (compressedGeometryRenderMethod == null) {
2533             compressedGeometryRenderMethod =
2534 		new CompressedGeometryRenderMethod();
2535         }
2536         return compressedGeometryRenderMethod;
2537     }
2538 
2539     /**
2540      * This returns the oriented shape3d RenderMethod
2541      */
2542     RenderMethod getOrientedShape3DRenderMethod() {
2543 	if (orientedShape3DRenderMethod == null) {
2544 	    orientedShape3DRenderMethod = new OrientedShape3DRenderMethod();
2545 	}
2546 	return orientedShape3DRenderMethod;
2547     }
2548 
2549     /**
2550      * This notifies MasterControl that the given view has been activated
2551      */
2552     private void viewActivate(View v) {
2553 
2554 	VirtualUniverse univ = v.universe;
2555 
2556 	if (univ == null) {
2557 	    return;
2558 	}
2559 
2560 	if (!views.contains(v) || !regUniverseList.contains(univ)) {
2561 	    registerView(v);
2562 	} else if (v.active) {
2563 	    evaluateAllCanvases();
2564 	    return;
2565 	}
2566 
2567 	if ((univ.activeViewCount == 0)) {
2568 	    univ.geometryStructure.resetConditionMet();
2569 	    univ.behaviorStructure.resetConditionMet();
2570 	}
2571 
2572 	if (v.isRunning) {
2573 	    numActiveViews++;
2574 	    univ.activeViewCount++;
2575 	    renderingAttributesStructure.updateThread.active = true;
2576 	    univ.transformStructure.updateThread.active = true;
2577 	    univ.geometryStructure.updateThread.active = true;
2578 	    univ.soundStructure.updateThread.active = true;
2579 	    univ.renderingEnvironmentStructure.updateThread.active = true;
2580 	}
2581 	univ.behaviorScheduler.active = true;
2582 	univ.behaviorStructure.updateThread.active = true;
2583 
2584 
2585 	activeUniverseList.addUnique(univ);
2586 
2587 	if (v.isRunning) {
2588 	    v.soundScheduler.activate();
2589 	    v.renderBin.updateThread.active = true;
2590 	}
2591 	v.active = true;
2592 
2593 	if (v.physicalEnvironment.activeViewRef++ == 0) {
2594 	    v.physicalEnvironment.inputsched.activate();
2595 	}
2596 
2597 
2598 	if (univ.getCurrentView() == null) {
2599 	    assignNewPrimaryView(univ);
2600 	}
2601 
2602 	evaluateAllCanvases();
2603 	v.inRenderThreadData = true;
2604 	threadListsChanged = true;
2605 	// Notify GeometryStructure to query visible atom again
2606 	// We should send message instead of just setting
2607 	// v.vDirtyMask = View.VISIBILITY_POLICY_DIRTY;
2608 	// since RenderBin may not run immediately next time.
2609 	// In this case the dirty flag will lost since
2610 	// updateViewCache() will reset it to 0.
2611 	v.renderBin.reactivateView = true;
2612     }
2613 
2614     /**
2615      * Release context associate with view
2616      */
2617     private void freeContext(View v) {
2618 	Canvas3D[][] canvasList = v.getCanvasList(false);
2619 
2620 	for (int j=canvasList.length-1; j>=0; j--) {
2621 	    for (int k=canvasList[j].length-1; k>=0; k--) {
2622 		Canvas3D cv = canvasList[j][k];
2623 		if (!cv.validCanvas) {
2624 		    if ((cv.screen != null) &&
2625 			(cv.screen.renderer != null)) {
2626 			rendererCleanupArgs[1] = cv;
2627 			rendererCleanupArgs[2] = FREECONTEXT_CLEANUP;
2628 			runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
2629 				   cv.screen.renderer);
2630 			rendererCleanupArgs[1] = null;
2631 		    }
2632 		}
2633 	    }
2634 	}
2635     }
2636 
2637     /**
2638      * This notifies MasterControl that the given view has been deactivated
2639      */
2640     private void viewDeactivate(View v) {
2641 
2642 	if (!views.contains(v) || !v.active) {
2643 	    v.active = false;
2644 	    evaluateAllCanvases();
2645 	    return;
2646 	}
2647 
2648 	VirtualUniverse univ = v.universe;
2649 
2650 	if (v.isRunning) {
2651 	    // if stopView() invoke before, no need to decrement count
2652 	    --numActiveViews;
2653 	    --univ.activeViewCount;
2654 	}
2655 
2656 	if (numActiveViews == 0) {
2657 	    renderingAttributesStructure.updateThread.active = false;
2658 	}
2659 
2660 	if (univ.activeViewCount == 0) {
2661 	    // check if destroyUniverseThread invoked before
2662 	    if (univ.behaviorScheduler != null) {
2663 		univ.behaviorScheduler.deactivate();
2664 		univ.transformStructure.updateThread.active = false;
2665 		univ.geometryStructure.updateThread.active = false;
2666 		univ.behaviorStructure.updateThread.active = false;
2667 		univ.soundStructure.updateThread.active = false;
2668 		univ.renderingEnvironmentStructure.updateThread.active
2669 		    = false;
2670 		activeUniverseList.remove(univ);
2671 	    }
2672 	}
2673 
2674 	v.soundScheduler.deactivate();
2675 	v.renderBin.updateThread.active = false;
2676 	v.active = false;
2677 	if (--v.physicalEnvironment.activeViewRef == 0) {
2678 	    v.physicalEnvironment.inputsched.deactivate();
2679 	}
2680 	assignNewPrimaryView(univ);
2681 
2682 
2683 	evaluateAllCanvases();
2684 
2685 	freeContext(v);
2686 
2687 	v.inRenderThreadData = false;
2688 	threadListsChanged = true;
2689     }
2690 
2691 
2692    /**
2693      * This notifies MasterControl to start given view
2694      */
2695     private void startView(View v) {
2696 
2697 	if (!views.contains(v) || v.isRunning || !v.active) {
2698 	    v.isRunning = true;
2699 	    return;
2700 	}
2701 
2702 	numActiveViews++;
2703 	renderingAttributesStructure.updateThread.active = true;
2704 
2705 	VirtualUniverse univ = v.universe;
2706 
2707 	univ.activeViewCount++;
2708 	univ.transformStructure.updateThread.active = true;
2709 	univ.geometryStructure.updateThread.active = true;
2710 	univ.soundStructure.updateThread.active = true;
2711 	univ.renderingEnvironmentStructure.updateThread.active = true;
2712 	v.renderBin.updateThread.active = true;
2713 	v.soundScheduler.activate();
2714 	v.isRunning = true;
2715 	if (univ.getCurrentView() == null) {
2716 	    assignNewPrimaryView(univ);
2717 	}
2718 	threadListsChanged = true;
2719     }
2720 
2721 
2722    /**
2723      * This notifies MasterControl to stop given view
2724      */
2725     private void stopView(View v) {
2726 	if (!views.contains(v) || !v.isRunning || !v.active) {
2727 	    v.isRunning = false;
2728 	    return;
2729 	}
2730 
2731 	if (--numActiveViews == 0) {
2732 	    renderingAttributesStructure.updateThread.active = false;
2733 	}
2734 	VirtualUniverse univ = v.universe;
2735 
2736 	if (--univ.activeViewCount == 0) {
2737 		univ.transformStructure.updateThread.active = false;
2738 		univ.geometryStructure.updateThread.active = false;
2739 		univ.renderingEnvironmentStructure.updateThread.active = false;
2740 		univ.soundStructure.updateThread.active = false;
2741 	}
2742 
2743 	v.renderBin.updateThread.active = false;
2744 	v.soundScheduler.deactivate();
2745 	v.isRunning = false;
2746 	assignNewPrimaryView(univ);
2747 	threadListsChanged = true;
2748     }
2749 
2750     // Call from user thread
2751     void addInputDeviceScheduler(InputDeviceScheduler ds) {
2752 	synchronized (inputDeviceThreads) {
2753 	    inputDeviceThreads.add(ds);
2754 	    if (inputDeviceThreads.size() == 1) {
2755 		timerThread.addInputDeviceSchedCond();
2756 	    }
2757 	}
2758 	postRequest(INPUTDEVICE_CHANGE, null);
2759     }
2760 
2761     // Call from user thread
2762     void removeInputDeviceScheduler(InputDeviceScheduler ds) {
2763 	inputDeviceThreads.remove(ds);
2764 	postRequest(INPUTDEVICE_CHANGE, null);
2765     }
2766 
2767     /**
2768      * Add an object to the mirror object list
2769      */
2770     void addMirrorObject(ObjectUpdate o) {
2771 	mirrorObjects.add(o);
2772     }
2773 
2774     /**
2775      * This updates any mirror objects.  It is called when threads
2776      * are done.
2777      */
2778     void updateMirrorObjects() {
2779 	ObjectUpdate objs[] = (ObjectUpdate []) mirrorObjects.toArray(false);
2780 	int sz = mirrorObjects.arraySize();
2781 
2782 	for (int i = 0; i< sz; i++) {
2783 	    objs[i].updateObject();
2784 	}
2785 	mirrorObjects.clear();
2786     }
2787 
2788 
2789     /**
2790      * This fun little method does all the hard work of setting up the
2791      * work thread list.
2792      */
2793     private void updateWorkThreads() {
2794 
2795 	stateWorkThreads.clear();
2796 	renderWorkThreads.clear();
2797 	requestRenderWorkThreads.clear();
2798 
2799 	// First the global rendering attributes structure update
2800 	if (numActiveViews > 0) {
2801 	    addToStateThreads(renderingAttributesStructure.getUpdateThreadData());
2802 	}
2803 
2804 	// Next, each of the transform structure updates
2805 	VirtualUniverse universes[] = (VirtualUniverse [])
2806 	                         activeUniverseList.toArray(false);
2807 	VirtualUniverse univ;
2808 	int i;
2809 	int size = activeUniverseList.arraySize();
2810 
2811 	for (i=size-1; i>=0; i--) {
2812 	    addToStateThreads(universes[i].transformStructure.getUpdateThreadData());
2813 	}
2814 	lastTransformStructureThread = stateWorkThreads.size();
2815 
2816 	// Next, the GeometryStructure, BehaviorStructure,
2817 	//       RenderingEnvironmentStructure, and SoundStructure
2818 	for (i=size-1; i>=0; i--) {
2819 	    univ = universes[i];
2820 	    addToStateThreads(univ.geometryStructure.getUpdateThreadData());
2821 	    addToStateThreads(univ.behaviorStructure.getUpdateThreadData());
2822 	    addToStateThreads(univ.renderingEnvironmentStructure.getUpdateThreadData());
2823 	    addToStateThreads(univ.soundStructure.getUpdateThreadData());
2824 	}
2825 
2826 	lastStructureUpdateThread = stateWorkThreads.size();
2827 
2828 	// Next, the BehaviorSchedulers
2829 	for (i=size-1; i>=0; i--) {
2830 	    addToStateThreads(universes[i].behaviorScheduler.
2831 			      getThreadData(null, null));
2832 	}
2833 
2834 
2835 	// Now InputDeviceScheduler
2836 
2837 	InputDeviceScheduler ds[] = (InputDeviceScheduler [])
2838 	                              inputDeviceThreads.toArray(true);
2839 	for (i=inputDeviceThreads.size()-1; i >=0; i--) {
2840 	    J3dThreadData threadData = ds[i].getThreadData();
2841 	    threadData.thread.active = true;
2842 	    addToStateThreads(threadData);
2843 	}
2844 
2845 	// Now the RenderBins and SoundSchedulers
2846 	View viewArr[] = (View []) views.toArray(false);
2847 	J3dThreadData thread;
2848 
2849 	for (i=views.size()-1; i>=0; i--) {
2850 	    View v = viewArr[i];
2851 	    if (v.active && v.isRunning) {
2852 		addToStateThreads(v.renderBin.getUpdateThreadData());
2853 		addToStateThreads(v.soundScheduler.getUpdateThreadData());
2854 	        Canvas3D canvasList[][] = v.getCanvasList(false);
2855 	        int longestScreenList = v.getLongestScreenList();
2856 		Object args[] = null;
2857 		// renderer render
2858 	        for (int j=0; j<longestScreenList; j++) {
2859 	            for (int k=0; k < canvasList.length; k++) {
2860 	                if (j < canvasList[k].length) {
2861 	                    Canvas3D cv = canvasList[k][j];
2862                             // Issue 131: setup renderer unless manualRendering
2863 			    if (cv.active && cv.isRunningStatus && !cv.manualRendering ) {
2864 				if (cv.screen.renderer == null) {
2865 				    continue;
2866 				}
2867 				thread = cv.screen.renderer.getThreadData(v, cv);
2868 				renderWorkThreads.add(thread);
2869 				args =	(Object []) thread.threadArgs;
2870 				args[0] = RENDER;
2871 				args[1] = cv;
2872 				args[2] = v;
2873 			    }
2874 			}
2875 		    }
2876 		}
2877 
2878 		// renderer swap
2879 	        for (int j=0; j<canvasList.length; j++) {
2880 		    for (int k=0; k < canvasList[j].length; k++) {
2881 			Canvas3D cv = canvasList[j][k];
2882 			// create swap thread only if there is at
2883 			// least one active canvas
2884                         // Issue 131: only if not manualRendering
2885 			if (cv.active && cv.isRunningStatus && !cv.manualRendering) {
2886 			    if (cv.screen.renderer == null) {
2887 				// Should not happen
2888 				continue;
2889 			    }
2890 			    thread = cv.screen.renderer.getThreadData(v, null);
2891 			    renderWorkThreads.add(thread);
2892 			    args = (Object []) thread.threadArgs;
2893 			    args[0] = SWAP;
2894 			    args[1] = v;
2895 			    args[2] = canvasList[j];
2896 			    break;
2897 			}
2898 		    }
2899 		}
2900 	    }
2901 	}
2902 
2903 	thread = null;
2904 
2905 	for (Enumeration e = Screen3D.deviceRendererMap.elements();
2906 	     e.hasMoreElements(); ) {
2907 	    Renderer rdr = (Renderer) e.nextElement();
2908 	    thread = rdr.getThreadData(null, null);
2909 	    requestRenderWorkThreads.add(thread);
2910 	    thread.threadOpts = J3dThreadData.CONT_THREAD;
2911 	    ((Object[]) thread.threadArgs)[0] = REQUESTRENDER;
2912 	}
2913 
2914 	if (thread != null) {
2915 	    thread.threadOpts |= J3dThreadData.WAIT_ALL_THREADS;
2916 	}
2917 
2918 	threadListsChanged = false;
2919 
2920 	//	 dumpWorkThreads();
2921     }
2922 
2923 
2924     void dumpWorkThreads() {
2925 	System.err.println("-----------------------------");
2926 	System.err.println("MasterControl/dumpWorkThreads");
2927 
2928 	J3dThreadData threads[];
2929 	int size = 0;
2930 
2931 	for (int k=0; k<3; k++) {
2932 	    switch (k) {
2933 	    case 0:
2934 		threads = (J3dThreadData []) stateWorkThreads.toArray(false);
2935 		size = stateWorkThreads.arraySize();
2936 		break;
2937 	    case 1:
2938 		threads = (J3dThreadData []) renderWorkThreads.toArray(false);
2939 		size = renderWorkThreads.arraySize();
2940 		break;
2941 	    default:
2942 		threads = (J3dThreadData []) requestRenderWorkThreads.toArray(false);
2943 		size = requestRenderWorkThreads.arraySize();
2944 		break;
2945 	    }
2946 
2947 	    for (int i=0; i<size; i++) {
2948 		J3dThreadData thread = threads[i];
2949 		System.err.println("Thread " + i + ": " + thread.thread);
2950 		System.err.println("\tOps: " + thread.threadOpts);
2951 		if (thread.threadArgs != null) {
2952 		    Object[] args = (Object[]) thread.threadArgs;
2953 		    System.err.print("\tArgs: ");
2954 		    for (int j=0; j<args.length; j++) {
2955 			System.err.print(args[j] + " ");
2956 		    }
2957 		}
2958 		System.err.println("");
2959 	    }
2960 	}
2961 	System.err.println("-----------------------------");
2962     }
2963 
2964 
2965     /**
2966      * A convienence wrapper function for various parts of the system
2967      * to force MC to run.
2968      */
2969     final void setWork() {
2970 	runMonitor(SET_WORK, null, null, null, null);
2971     }
2972 
2973     final void setWorkForRequestRenderer() {
2974         runMonitor(SET_WORK_FOR_REQUEST_RENDERER, null, null, null, null);
2975     }
2976 
2977     /**
2978      * Call from GraphicsConfigTemplate to evaluate current
2979      * capabilities using Renderer thread to invoke native
2980      * graphics library functions. This avoid MT-safe problem
2981      * when using thread directly invoke graphics functions.
2982      */
2983     void sendRenderMessage(GraphicsConfiguration gc,
2984 			   Object arg, Integer mtype) {
2985 	Renderer rdr = createRenderer(gc);
2986 	J3dMessage renderMessage = new J3dMessage();
2987 	renderMessage.threads = J3dThread.RENDER_THREAD;
2988 	renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
2989 	renderMessage.universe = null;
2990 	renderMessage.view = null;
2991 	renderMessage.args[0] = null;
2992 	renderMessage.args[1] = arg;
2993 	renderMessage.args[2] = mtype;
2994 	rdr.rendererStructure.addMessage(renderMessage);
2995 	setWorkForRequestRenderer();
2996     }
2997 
2998     // Issue for Issue 175
2999     // Pass DestroyCtxAndOffScreenBuffer to the Renderer thread for execution.
3000     void sendDestroyCtxAndOffScreenBuffer(Canvas3D c) {
3001         // Assertion check. Look for comment in sendCreateOffScreenBuffer.
3002         GraphicsDevice gd = c.graphicsConfiguration.getDevice();
3003 	assert Screen3D.deviceRendererMap.get(gd) != null;
3004 
3005 	synchronized (mcThreadLock) {
3006             // Issue 364: create master control thread if needed
3007 	    createMasterControlThread();
3008             assert mcThread != null;
3009 
3010 	    Renderer rdr = createRenderer(c.graphicsConfiguration);
3011 	    J3dMessage createMessage = new J3dMessage();
3012 	    createMessage.threads = J3dThread.RENDER_THREAD;
3013 	    createMessage.type = J3dMessage.DESTROY_CTX_AND_OFFSCREENBUFFER;
3014 	    createMessage.universe = null;
3015 	    createMessage.view = null;
3016 	    createMessage.args[0] = c;
3017             // Fix for issue 340: send display, drawable & ctx in msg
3018             createMessage.args[1] = new Long(c.screen.display);
3019             createMessage.args[2] = c.drawable;
3020             createMessage.args[3] = c.ctx;
3021 	    rdr.rendererStructure.addMessage(createMessage);
3022 	    synchronized (requestObjList) {
3023 		setWorkForRequestRenderer();
3024 		pendingRequest = true;
3025 	    }
3026         }
3027     }
3028 
3029     // Fix for Issue 18
3030     // Pass CreateOffScreenBuffer to the Renderer thread for execution.
3031     void sendCreateOffScreenBuffer(Canvas3D c) {
3032 	// Assertion check that the renderer has already been created.
3033 	// If it hasn't, this is very, very bad because it opens up
3034 	// the possibility of an MT race condition since this method
3035 	// can be called from the user's thread, possibly at the same
3036 	// time as the MasterControl thread is trying to create a new
3037 	// Renderer.  Fortunately, this should never happen since both
3038 	// the GraphicsTemplate3D methods that return a valid Graphics
3039 	// Configuration and the Canvas3D constructor will ultimately
3040 	// cause a renderer to be created via sendRenderMessage().
3041 	GraphicsDevice gd = c.graphicsConfiguration.getDevice();
3042 	J3dDebug.doAssert((Screen3D.deviceRendererMap.get(gd) != null),
3043 			  "Screen3D.deviceRendererMap.get(gd) != null");
3044 
3045 	synchronized (mcThreadLock) {
3046             // Create master control thread if needed
3047 	    createMasterControlThread();
3048             assert mcThread != null;
3049 
3050 	    // Fix for Issue 72 : call createRenderer rather than getting
3051 	    // the renderer from the canvas.screen object
3052 	    Renderer rdr = createRenderer(c.graphicsConfiguration);
3053 	    J3dMessage createMessage = new J3dMessage();
3054 	    createMessage.threads = J3dThread.RENDER_THREAD;
3055 	    createMessage.type = J3dMessage.CREATE_OFFSCREENBUFFER;
3056 	    createMessage.universe = null;
3057 	    createMessage.view = null;
3058 	    createMessage.args[0] = c;
3059 	    rdr.rendererStructure.addMessage(createMessage);
3060 	    synchronized (requestObjList) {
3061 		setWorkForRequestRenderer();
3062 		pendingRequest = true;
3063 	    }
3064 	}
3065     }
3066 
3067     // Issue 347 - Pass AllocateCanvasId to the Renderer thread for execution
3068     void sendAllocateCanvasId(Canvas3D c) {
3069         synchronized (mcThreadLock) {
3070             // Issue 364: create master control thread if needed
3071             createMasterControlThread();
3072             assert mcThread != null;
3073 
3074             Renderer rdr = createRenderer(c.graphicsConfiguration);
3075             J3dMessage createMessage = new J3dMessage();
3076             createMessage.threads = J3dThread.RENDER_THREAD;
3077             createMessage.type = J3dMessage.ALLOCATE_CANVASID;
3078             createMessage.universe = null;
3079             createMessage.view = null;
3080             createMessage.args[0] = c;
3081             rdr.rendererStructure.addMessage(createMessage);
3082             synchronized (requestObjList) {
3083                 setWorkForRequestRenderer();
3084                 pendingRequest = true;
3085             }
3086         }
3087     }
3088 
3089     // Issue 347 - Pass AllocateCanvasId to the Renderer thread for execution
3090     void sendFreeCanvasId(Canvas3D c) {
3091         synchronized (mcThreadLock) {
3092             // Issue 364: create master control thread if needed
3093             createMasterControlThread();
3094             assert mcThread != null;
3095 
3096             Renderer rdr = createRenderer(c.graphicsConfiguration);
3097             J3dMessage createMessage = new J3dMessage();
3098             createMessage.threads = J3dThread.RENDER_THREAD;
3099             createMessage.type = J3dMessage.FREE_CANVASID;
3100             createMessage.universe = null;
3101             createMessage.view = null;
3102             createMessage.args[0] = c;
3103             rdr.rendererStructure.addMessage(createMessage);
3104             synchronized (requestObjList) {
3105                 setWorkForRequestRenderer();
3106                 pendingRequest = true;
3107             }
3108         }
3109     }
3110 
3111 
3112     /**
3113      * This is the MasterControl work method for Java 3D
3114      */
3115     void doWork() {
3116 	runMonitor(CHECK_FOR_WORK, null, null, null, null);
3117 
3118 	synchronized (timeLock) {
3119 	    synchronized (requestObjList) {
3120 		if (pendingRequest) {
3121 		    handlePendingRequest();
3122 		}
3123 	    }
3124 	}
3125 
3126 	if (!running) {
3127 	    return;
3128 	}
3129 
3130 	if (threadListsChanged) { // Check for new Threads
3131 	    updateWorkThreads();
3132 	}
3133 
3134 	synchronized (timeLock) {
3135 	    // This is neccesary to prevent updating
3136 	    // thread.lastUpdateTime from user thread
3137 	    // in sendMessage() or sendRunMessage()
3138 	    updateTimeValues();
3139 	}
3140 
3141 	//This is temporary until the view model is updated
3142 	View v[] = (View []) views.toArray(false);
3143 	for (int i=views.size()-1; i>=0; i--) {
3144 	    if (v[i].active) {
3145 		v[i].updateViewCache();
3146 		// update OrientedShape3D
3147 		if ((v[i].viewCache.vcDirtyMask != 0 &&
3148 		     !v[i].renderBin.orientedRAs.isEmpty()) ||
3149 		    (v[i].renderBin.cachedDirtyOrientedRAs != null &&
3150 		     !v[i].renderBin.cachedDirtyOrientedRAs.isEmpty())) {
3151 		    v[i].renderBin.updateOrientedRAs();
3152 		}
3153 	    }
3154 	}
3155 
3156 	runMonitor(RUN_THREADS, stateWorkThreads, renderWorkThreads,
3157 		   requestRenderWorkThreads, null);
3158 
3159 	if (renderOnceList.size() > 0) {
3160 	    clearRenderOnceList();
3161 	}
3162 
3163 	manageMemory();
3164 
3165     }
3166 
3167     private void handlePendingRequest() {
3168 
3169 	Object objs[];
3170 	Integer types[];
3171 	int size;
3172 	boolean rendererRun = false;
3173 
3174 	objs = requestObjList.toArray(false);
3175 	types = (Integer []) requestTypeList.toArray(false);
3176 	size = requestObjList.size();
3177 
3178 	for (int i=0; i < size; i++) {
3179 	    // need to process request in order
3180 	    Integer type = types[i];
3181 	    Object o = objs[i];
3182            if (type == RESET_CANVAS) {
3183                Canvas3D cv = (Canvas3D) o;
3184 		if ((cv.screen != null) &&
3185 		    (cv.screen.renderer != null)) {
3186 		    rendererCleanupArgs[1] = o;
3187 		    rendererCleanupArgs[2] = RESETCANVAS_CLEANUP;
3188 		    runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
3189 			       cv.screen.renderer);
3190 		    rendererCleanupArgs[1] = null;
3191 		}
3192                cv.reset();
3193 	       cv.view = null;
3194 	       cv.computeViewCache();
3195            }
3196 	   else if (type == ACTIVATE_VIEW) {
3197 		viewActivate((View) o);
3198 	   }
3199 	   else if (type == DEACTIVATE_VIEW) {
3200 		viewDeactivate((View) o);
3201 	    } else if (type == REEVALUATE_CANVAS) {
3202 		evaluateAllCanvases();
3203 	    } else if (type == INPUTDEVICE_CHANGE) {
3204 		inputDeviceThreads.clearMirror();
3205 		threadListsChanged = true;
3206 	    } else if (type == START_VIEW) {
3207 		startView((View) o);
3208 	    } else if (type == STOP_VIEW) {
3209 		View v = (View) o;
3210 		// Collision takes 3 rounds to finish its request
3211 		if (++v.stopViewCount > 4) {
3212 		    v.stopViewCount = -1; // reset counter
3213 		    stopView(v);
3214 		} else {
3215 		    tempViewList.add(v);
3216 		}
3217 	    } else if (type == UNREGISTER_VIEW) {
3218 		unregisterView((View) o);
3219 	    } else if (type == PHYSICAL_ENV_CHANGE) {
3220 		evaluatePhysicalEnv((View) o);
3221 	    } else if (type == EMPTY_UNIVERSE) {
3222 		// Issue 81: We need to process this message as long
3223 		// as there are no views associated with this
3224 		// universe. Previously, this message was ignored if
3225 		// there were views associated with *any* universe,
3226 		// which led to a memory / thread leak.
3227 	        boolean foundView = false;
3228 		VirtualUniverse univ = (VirtualUniverse) o;
3229 		View v[] = (View []) views.toArray(false);
3230 		for (int j = views.size() - 1; j >= 0; j--) {
3231 		    if (v[j].universe == univ) {
3232 			foundView = true;
3233 			break;
3234 		    }
3235 		}
3236 		if (!foundView) {
3237 		    destroyUniverseThreads(univ);
3238 		    threadListsChanged = true;
3239 		}
3240 	    } else if (type == START_RENDERER) {
3241                 if (o instanceof Canvas3D) {
3242                     Canvas3D c3d = (Canvas3D) o;
3243                     if (!c3d.isFatalError()) {
3244                         c3d.isRunningStatus = true;
3245                     }
3246                 } else {
3247                     ((Renderer) o).userStop = false;
3248                 }
3249 		threadListsChanged = true;
3250 	    } else if (type == STOP_RENDERER) {
3251 		if (o instanceof Canvas3D) {
3252 		    ((Canvas3D) o).isRunningStatus = false;
3253 		} else {
3254 		    ((Renderer) o).userStop = true;
3255 		}
3256 		threadListsChanged = true;
3257 	    } else if (type == RENDER_ONCE) {
3258 		View v = (View) o;
3259 		// temporary start View for renderonce
3260 		// it will stop afterwards
3261 		startView(v);
3262 		renderOnceList.add(v);
3263 		sendRunMessage(v, J3dThread.UPDATE_RENDER);
3264 		threadListsChanged = true;
3265 		rendererRun = true;
3266 	    } else if (type == FREE_CONTEXT) {
3267 		Canvas3D cv = (Canvas3D ) ((Object []) o)[0];
3268 		if ((cv.screen != null) &&
3269 		    (cv.screen.renderer != null)) {
3270 		    rendererCleanupArgs[1] = o;
3271 		    rendererCleanupArgs[2] = REMOVECTX_CLEANUP;
3272 		    runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
3273 			       cv.screen.renderer);
3274 		    rendererCleanupArgs[1] = null;
3275 		}
3276 		rendererRun = true;
3277             } else if (type == FREE_DRAWING_SURFACE) {
3278                 Pipeline.getPipeline().freeDrawingSurfaceNative(o);
3279             } else if (type == GETBESTCONFIG) {
3280 		GraphicsConfiguration gc = ((GraphicsConfiguration [])
3281 			  ((GraphicsConfigTemplate3D) o).testCfg)[0];
3282 		sendRenderMessage(gc, o, type);
3283 		rendererRun = true;
3284 	    } else if (type == ISCONFIGSUPPORT) {
3285 		GraphicsConfiguration  gc = (GraphicsConfiguration)
3286 			  ((GraphicsConfigTemplate3D) o).testCfg;
3287 		sendRenderMessage(gc, o, type);
3288 		rendererRun = true;
3289 	    } else if ((type == SET_GRAPHICSCONFIG_FEATURES) ||
3290 		       (type == SET_QUERYPROPERTIES)) {
3291 		GraphicsConfiguration gc = (GraphicsConfiguration)
3292 		          ((Canvas3D) o).graphicsConfiguration;
3293 		sendRenderMessage(gc, o, type);
3294 		rendererRun = true;
3295 	    } else if (type == SET_VIEW) {
3296 		Canvas3D cv = (Canvas3D) o;
3297 		cv.view = cv.pendingView;
3298 		cv.computeViewCache();
3299 	    }
3300 	}
3301 
3302 	// Do it only after all universe/View is register
3303 	for (int i=0; i < size; i++) {
3304 	    Integer type = types[i];
3305 	    if (type == FREE_MESSAGE) {
3306 		if (objs[i] instanceof VirtualUniverse) {
3307 		    VirtualUniverse u = (VirtualUniverse) objs[i];
3308 		    if (!regUniverseList.contains(u)) {
3309 			emptyMessageList(u.behaviorStructure, null);
3310 			emptyMessageList(u.geometryStructure, null);
3311 			emptyMessageList(u.soundStructure, null);
3312 			emptyMessageList(u.renderingEnvironmentStructure, null);
3313 		    }
3314 		} else if (objs[i] instanceof View) {
3315 		    View v = (View) objs[i];
3316 		    if (!views.contains(v)) {
3317 			emptyMessageList(v.soundScheduler, v);
3318 			emptyMessageList(v.renderBin, v);
3319 			if (v.resetUnivCount == v.universeCount) {
3320 			    v.reset();
3321 			    v.universe = null;
3322 			    if (running == false) {
3323 				// MC is about to terminate
3324 
3325 				/*
3326 				// Don't free list cause there may
3327 				// have some other thread returning ID
3328 				// after it.
3329 				FreeListManager.clearList(FreeListManager.DISPLAYLIST);
3330 				FreeListManager.clearList(FreeListManager.TEXTURE2D);
3331 				FreeListManager.clearList(FreeListManager.TEXTURE3D);
3332 
3333 				synchronized (textureIdLock) {
3334 				    textureIdCount = 0;
3335 				}
3336 				*/
3337 			    }
3338 			}
3339 		    }
3340 		}
3341 
3342 	    }
3343 
3344 	}
3345 	requestObjList.clear();
3346 	requestTypeList.clear();
3347 
3348 	size = tempViewList.size();
3349 	if (size > 0) {
3350 	    if (running) {
3351 		for (int i=0; i < size; i++) {
3352 		    requestTypeList.add(STOP_VIEW);
3353 		    requestObjList.add(tempViewList.get(i));
3354 		}
3355 		setWork();
3356 	    } else { // MC will shutdown
3357 		for (int i=0; i < size; i++) {
3358 		    View v = (View) tempViewList.get(i);
3359 		    v.stopViewCount = -1;
3360 		    v.isRunning = false;
3361 		}
3362 	    }
3363 	    tempViewList.clear();
3364 	    pendingRequest = true;
3365 	} else {
3366 	    pendingRequest = rendererRun || (requestObjList.size() > 0);
3367 
3368 	}
3369 
3370 	size = freeMessageList.size();
3371 	if (size > 0) {
3372 	    for (int i=0; i < size; i++) {
3373 		requestTypeList.add(FREE_MESSAGE);
3374 		requestObjList.add(freeMessageList.get(i));
3375 	    }
3376 	    pendingRequest = true;
3377 	    freeMessageList.clear();
3378 	}
3379 	if (!running && (renderOnceList.size() > 0)) {
3380 	    clearRenderOnceList();
3381 	}
3382 
3383 	if (pendingRequest) {
3384 	    setWork();
3385 	}
3386 
3387 	if (rendererRun || requestRenderWorkToDo) {
3388 	    running = true;
3389 	}
3390 
3391     }
3392 
3393     private void clearRenderOnceList() {
3394 	for (int i=renderOnceList.size()-1; i>=0; i--) {
3395 	    View v = (View) renderOnceList.get(i);
3396 	    v.renderOnceFinish = true;
3397 	    // stop after render once
3398 	    stopView(v);
3399 	}
3400 	renderOnceList.clear();
3401 	threadListsChanged = true;
3402 
3403     }
3404 
3405     synchronized void runMonitor(int action,
3406 				 UnorderList stateThreadList,
3407 				 UnorderList renderThreadList,
3408 				 UnorderList requestRenderThreadList,
3409 				 J3dThread nthread) {
3410 
3411         switch (action) {
3412 	case RUN_THREADS:
3413 	    int currentStateThread = 0;
3414 	    int currentRenderThread = 0;
3415 	    int currentRequestRenderThread = 0;
3416 	    View view;
3417 	    boolean done;
3418 	    J3dThreadData thread;
3419 	    J3dThreadData renderThreads[] = (J3dThreadData [])
3420 		                             renderThreadList.toArray(false);
3421 	    J3dThreadData stateThreads[] = (J3dThreadData [])
3422 		                            stateThreadList.toArray(false);
3423 	    J3dThreadData requestRenderThreads[] = (J3dThreadData [])
3424 		                       requestRenderThreadList.toArray(false);
3425 	    int renderThreadSize = renderThreadList.arraySize();
3426 	    int stateThreadSize = stateThreadList.arraySize();
3427 	    int requestRenderThreadSize = requestRenderThreadList.arraySize();
3428 
3429 	    done = false;
3430 
3431 	    //lock all the needed geometry and image component
3432 	    View[] allView = (View []) views.toArray(false);
3433 	    View currentV;
3434 	    int i;
3435 
3436 	    if (lockGeometry)
3437 	    {
3438 		for( i = views.arraySize()-1; i >= 0; i--) {
3439 		    currentV = allView[i];
3440 		    currentV.renderBin.lockGeometry();
3441 		}
3442 	    }
3443 
3444 	    while (!done) {
3445 		// First try a RenderThread
3446 		while (!renderWaiting &&
3447 		       currentRenderThread != renderThreadSize) {
3448 		    thread = renderThreads[currentRenderThread++];
3449 		    if (!thread.needsRun) {
3450 			continue;
3451 		    }
3452 		    if ((thread.threadOpts & J3dThreadData.START_TIMER) != 0) {
3453 		        view = (View)((Object[])thread.threadArgs)[2];
3454 		        view.frameNumber++;
3455 		        view.startTime = J3dClock.currentTimeMillis();
3456 		    }
3457 
3458 
3459 		    renderPending++;
3460 
3461 		    if (cpuLimit == 1) {
3462 			thread.thread.args = (Object[])thread.threadArgs;
3463 			thread.thread.doWork(currentTime);
3464 		    } else {
3465 			threadPending++;
3466 			thread.thread.runMonitor(J3dThread.RUN,
3467 						 currentTime,
3468 						 (Object[])thread.threadArgs);
3469 		    }
3470 
3471 		    if ((thread.threadOpts & J3dThreadData.STOP_TIMER) != 0) {
3472 			view = (View)((Object[])thread.threadArgs)[3];
3473 			timestampUpdateList.add(view);
3474 		    }
3475 
3476 		    if ((thread.threadOpts & J3dThreadData.LAST_STOP_TIMER) != 0) {
3477 			// release lock on locked geometry and image component
3478 			for( i = 0; i < views.arraySize(); i++) {
3479 			    currentV = allView[i];
3480 			    currentV.renderBin.releaseGeometry();
3481 			}
3482 		    }
3483 
3484 		    if ((cpuLimit != 1) &&
3485 			(thread.threadOpts &
3486 			 J3dThreadData.WAIT_ALL_THREADS) != 0) {
3487 
3488 			renderWaiting = true;
3489 		    }
3490 
3491 
3492 		    if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
3493 			state = WAITING_FOR_CPU;
3494 			try {
3495 			    wait();
3496 			} catch (InterruptedException e) {
3497 			    System.err.println(e);
3498 			}
3499 			state = RUNNING;
3500 		    }
3501 
3502 		}
3503 		// Now try state threads
3504 		while (!stateWaiting &&
3505 		       currentStateThread != stateThreadSize) {
3506 		    thread = stateThreads[currentStateThread++];
3507 
3508 		    if (!thread.needsRun) {
3509 			continue;
3510 		    }
3511 
3512 		    statePending++;
3513 
3514 		    if (cpuLimit == 1) {
3515 			thread.thread.args = (Object[])thread.threadArgs;
3516 			thread.thread.doWork(currentTime);
3517 		    } else {
3518 			threadPending++;
3519 			thread.thread.runMonitor(J3dThread.RUN,
3520 						 currentTime,
3521 						 (Object[])thread.threadArgs);
3522 		    }
3523 		    if (cpuLimit != 1 && (thread.threadOpts &
3524 			 J3dThreadData.WAIT_ALL_THREADS) != 0) {
3525 			stateWaiting = true;
3526 		    }
3527 
3528 		    if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
3529 			// Fix bug 4686766 - always allow
3530 			// renderer thread to continue if not finish
3531 			// geomLock can release for Behavior thread to
3532 			// continue.
3533 			if (currentRenderThread == renderThreadSize) {
3534 			    state = WAITING_FOR_CPU;
3535 			    try {
3536 				wait();
3537 			    } catch (InterruptedException e) {
3538 				System.err.println(e);
3539 			    }
3540 			    state = RUNNING;
3541 			} else {
3542 			    // Run renderer thread next time
3543 			    break;
3544 			}
3545 
3546 		    }
3547 		}
3548 
3549 		// Now try requestRender threads
3550                 if (!renderWaiting &&
3551                      (currentRenderThread == renderThreadSize)) {
3552                     currentRequestRenderThread = 0;
3553                     while (!renderWaiting &&
3554                            (currentRequestRenderThread !=
3555                                 requestRenderThreadSize)) {
3556 
3557                         thread =
3558 			   requestRenderThreads[currentRequestRenderThread++];
3559 
3560                         renderPending++;
3561 
3562                         if (cpuLimit == 1) {
3563                             thread.thread.args = (Object[])thread.threadArgs;
3564                             thread.thread.doWork(currentTime);
3565                         } else {
3566                             threadPending++;
3567                             thread.thread.runMonitor(J3dThread.RUN,
3568                                                 currentTime,
3569                                                 (Object[])thread.threadArgs);
3570                         }
3571                         if (cpuLimit != 1 && (thread.threadOpts &
3572                                 J3dThreadData.WAIT_ALL_THREADS) != 0) {
3573                             renderWaiting = true;
3574                         }
3575                         if (cpuLimit != 1 && cpuLimit <= threadPending) {
3576                             state = WAITING_FOR_CPU;
3577                             try {
3578                                 wait();
3579                             } catch (InterruptedException e) {
3580                                 System.err.println(e);
3581                             }
3582 			    state = RUNNING;
3583                         }
3584                     }
3585                 }
3586 
3587 		if (cpuLimit != 1) {
3588 		    if ((renderWaiting &&
3589 			 (currentStateThread == stateThreadSize)) ||
3590 		          (stateWaiting &&
3591 		            currentRenderThread == renderThreadSize) ||
3592 		    	    (renderWaiting && stateWaiting)) {
3593 			if (!requestRenderWorkToDo) {
3594 		            state = WAITING_FOR_THREADS;
3595 		            try {
3596 			        wait();
3597 		            } catch (InterruptedException e) {
3598 			        System.err.println(e);
3599 		            }
3600 			    state = RUNNING;
3601 			}
3602 			requestRenderWorkToDo = false;
3603 		    }
3604 		}
3605 
3606                 if ((currentStateThread == stateThreadSize) &&
3607                     (currentRenderThread == renderThreadSize) &&
3608                     (currentRequestRenderThread == requestRenderThreadSize) &&
3609 		    (threadPending == 0)) {
3610 		    for (int k=timestampUpdateList.size()-1; k >=0;
3611 			 k--) {
3612 			View v = (View) timestampUpdateList.get(k);
3613 			v.setFrameTimingValues();
3614 			v.universe.behaviorStructure.incElapsedFrames();
3615 		    }
3616 		    timestampUpdateList.clear();
3617 		    updateMirrorObjects();
3618 		    done = true;
3619 
3620                     if (isStatsLoggable(Level.INFO)) {
3621                         // Instrumentation of Java 3D renderer
3622                         logTimes();
3623                     }
3624 		}
3625 	    }
3626 	    break;
3627 
3628 	case THREAD_DONE:
3629 	    if (state != WAITING_FOR_RENDERER_CLEANUP) {
3630 
3631 		threadPending--;
3632                 assert threadPending >= 0 : ("threadPending = " + threadPending);
3633                 if (nthread.type == J3dThread.RENDER_THREAD) {
3634 		    View v = (View) nthread.args[3];
3635 		    if (v != null) { // STOP_TIMER
3636 			v.stopTime = J3dClock.currentTimeMillis();
3637 		    }
3638 
3639 		    if (--renderPending == 0) {
3640 			renderWaiting = false;
3641 		    }
3642                     assert renderPending >= 0 : ("renderPending = " + renderPending);
3643 		} else {
3644 		    if (--statePending == 0) {
3645 			stateWaiting = false;
3646 		    }
3647                     assert statePending >= 0 : ("statePending = " + statePending);
3648 		}
3649 		if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS) {
3650 		    notify();
3651 		}
3652 	    } else {
3653 		notify();
3654 		state = RUNNING;
3655 	    }
3656 	    break;
3657 
3658 	case CHECK_FOR_WORK:
3659 	    if (!workToDo) {
3660 		state = SLEEPING;
3661                 // NOTE: this could wakeup spuriously (see issue 279), but it
3662                 // will not cause any problems.
3663 		try {
3664 		    wait();
3665 		} catch (InterruptedException e) {
3666 		    System.err.println(e);
3667 		}
3668 	        state = RUNNING;
3669 	    }
3670 	    workToDo = false;
3671 	    break;
3672 
3673 	case SET_WORK:
3674 	    workToDo = true;
3675 	    if (state == SLEEPING) {
3676 		notify();
3677 	    }
3678 	    break;
3679 
3680         case SET_WORK_FOR_REQUEST_RENDERER:
3681 	    requestRenderWorkToDo = true;
3682 	    workToDo = true;
3683 	    if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS ||
3684 			state == SLEEPING) {
3685 		notify();
3686 	    }
3687 	    break;
3688 
3689 	case RUN_RENDERER_CLEANUP:
3690 	    nthread.runMonitor(J3dThread.RUN, currentTime,
3691 			       rendererCleanupArgs);
3692 	    state = WAITING_FOR_RENDERER_CLEANUP;
3693             // Issue 279 - loop until state is set to running
3694             while (state != RUNNING) {
3695                 try {
3696                     wait();
3697                 } catch (InterruptedException e) {
3698                     System.err.println(e);
3699                 }
3700             }
3701 	    break;
3702 
3703         default:
3704             // Should never get here
3705             assert false : "missing case in switch statement";
3706         }
3707     }
3708 
3709     // Static initializer
3710     static {
3711         // create ThreadGroup
3712         java.security.AccessController.doPrivileged(
3713             new java.security.PrivilegedAction() {
3714                 public Object run() {
3715                     ThreadGroup parent;
3716                     Thread thread = Thread.currentThread();
3717                     threadPriority = thread.getPriority();
3718                     rootThreadGroup = thread.getThreadGroup();
3719                     while ((parent = rootThreadGroup.getParent()) != null) {
3720                         rootThreadGroup = parent;
3721                     }
3722                     rootThreadGroup = new ThreadGroup(rootThreadGroup,
3723                             "Java3D");
3724                     // use the default maximum group priority
3725                     return null;
3726                 }
3727         });
3728 
3729         // Initialize loggers
3730         try {
3731             initLoggers();
3732         } catch (RuntimeException ex) {
3733             System.err.println(ex);
3734         }
3735     }
3736 
3737 
3738     static String mtype[] = {
3739         "INSERT_NODES",
3740         "REMOVE_NODES",
3741         "RUN",
3742         "TRANSFORM_CHANGED",
3743         "UPDATE_VIEW",
3744         "STOP_THREAD",
3745         "COLORINGATTRIBUTES_CHANGED",
3746         "LINEATTRIBUTES_CHANGED",
3747         "POINTATTRIBUTES_CHANGED",
3748         "POLYGONATTRIBUTES_CHANGED",
3749         "RENDERINGATTRIBUTES_CHANGED",
3750         "TEXTUREATTRIBUTES_CHANGED",
3751         "TRANSPARENCYATTRIBUTES_CHANGED",
3752         "MATERIAL_CHANGED",
3753         "TEXCOORDGENERATION_CHANGED",
3754         "TEXTURE_CHANGED",
3755         "MORPH_CHANGED",
3756         "GEOMETRY_CHANGED",
3757         "APPEARANCE_CHANGED",
3758         "LIGHT_CHANGED",
3759         "BACKGROUND_CHANGED",
3760         "CLIP_CHANGED",
3761         "FOG_CHANGED",
3762         "BOUNDINGLEAF_CHANGED",
3763         "SHAPE3D_CHANGED",
3764         "TEXT3D_TRANSFORM_CHANGED",
3765         "TEXT3D_DATA_CHANGED",
3766         "SWITCH_CHANGED",
3767         "COND_MET",
3768         "BEHAVIOR_ENABLE",
3769         "BEHAVIOR_DISABLE",
3770         "INSERT_RENDERATOMS",
3771         "ORDERED_GROUP_INSERTED",
3772         "ORDERED_GROUP_REMOVED",
3773         "COLLISION_BOUND_CHANGED",
3774         "REGION_BOUND_CHANGED",
3775         "MODELCLIP_CHANGED",
3776         "BOUNDS_AUTO_COMPUTE_CHANGED",
3777         "SOUND_ATTRIB_CHANGED",
3778         "AURALATTRIBUTES_CHANGED",
3779         "SOUNDSCAPE_CHANGED",
3780         "ALTERNATEAPPEARANCE_CHANGED",
3781         "RENDER_OFFSCREEN",
3782         "RENDER_RETAINED",
3783         "RENDER_IMMEDIATE",
3784         "SOUND_STATE_CHANGED",
3785         "ORIENTEDSHAPE3D_CHANGED",
3786         "TEXTURE_UNIT_STATE_CHANGED",
3787         "UPDATE_VIEWPLATFORM",
3788         "BEHAVIOR_ACTIVATE",
3789         "GEOMETRYARRAY_CHANGED",
3790         "MEDIA_CONTAINER_CHANGED",
3791         "RESIZE_CANVAS",
3792         "TOGGLE_CANVAS",
3793         "IMAGE_COMPONENT_CHANGED",
3794         "SCHEDULING_INTERVAL_CHANGED",
3795         "VIEWSPECIFICGROUP_CHANGED",
3796         "VIEWSPECIFICGROUP_INIT",
3797         "VIEWSPECIFICGROUP_CLEAR",
3798         "ORDERED_GROUP_TABLE_CHANGED",
3799         "BEHAVIOR_REEVALUATE",
3800         "CREATE_OFFSCREENBUFFER",
3801         "DESTROY_CTX_AND_OFFSCREENBUFFER",
3802         "SHADER_ATTRIBUTE_CHANGED",
3803         "SHADER_ATTRIBUTE_SET_CHANGED",
3804         "SHADER_APPEARANCE_CHANGED",
3805         "ALLOCATE_CANVASID",
3806         "FREE_CANVASID",
3807     };
3808 
3809     private String dumpThreads(int threads) {
3810         StringBuffer strBuf = new StringBuffer();
3811         strBuf.append("threads:");
3812 	//dump Threads type
3813         if ((threads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
3814             strBuf.append(" BEHAVIOR_SCHEDULER");
3815         }
3816         if ((threads & J3dThread.SOUND_SCHEDULER) != 0) {
3817             strBuf.append(" SOUND_SCHEDULER");
3818         }
3819         if ((threads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
3820             strBuf.append(" INPUT_DEVICE_SCHEDULER");
3821         }
3822         if ((threads & J3dThread.RENDER_THREAD) != 0) {
3823             strBuf.append(" RENDER_THREAD");
3824         }
3825         if ((threads & J3dThread.UPDATE_GEOMETRY) != 0) {
3826             strBuf.append(" UPDATE_GEOMETRY");
3827         }
3828         if ((threads & J3dThread.UPDATE_RENDER) != 0) {
3829             strBuf.append(" UPDATE_RENDER");
3830         }
3831         if ((threads & J3dThread.UPDATE_BEHAVIOR) != 0) {
3832             strBuf.append(" UPDATE_BEHAVIOR");
3833         }
3834         if ((threads & J3dThread.UPDATE_SOUND) != 0) {
3835             strBuf.append(" UPDATE_SOUND");
3836         }
3837         if ((threads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
3838             strBuf.append(" UPDATE_RENDERING_ATTRIBUTES");
3839         }
3840         if ((threads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
3841             strBuf.append(" UPDATE_RENDERING_ENVIRONMENT");
3842         }
3843         if ((threads & J3dThread.UPDATE_TRANSFORM) != 0) {
3844             strBuf.append(" UPDATE_TRANSFORM");
3845         }
3846 
3847         return strBuf.toString();
3848     }
3849 
3850     // Method to log the specified message. Note that the caller
3851     // should check for isCoreLoggable(FINEST) before calling
3852     private void dumpMessage(String str, J3dMessage m) {
3853         StringBuffer strBuf = new StringBuffer();
3854         strBuf.append(str).append(" ");
3855         if (m.type >= 0 && m.type < mtype.length) {
3856             strBuf.append(mtype[m.type]);
3857         } else {
3858             strBuf.append("<UNKNOWN>");
3859         }
3860         strBuf.append("  ").append(dumpThreads(m.threads));
3861         getCoreLogger().finest(strBuf.toString());
3862     }
3863 
3864 
3865     int frameCount = 0;
3866     private int frameCountCutoff = 100;
3867 
3868     private void manageMemory() {
3869 	if (++frameCount > frameCountCutoff) {
3870 	    FreeListManager.manageLists();
3871 	    frameCount = 0;
3872 	}
3873     }
3874 
3875     /**
3876      * Yields the current thread, by sleeping for a small amount of
3877      * time.  Unlike <code>Thread.yield()</code>, this method
3878      * guarantees that the current thread will yield to another thread
3879      * waiting to run.  It also ensures that the other threads will
3880      * run for at least a small amount of time before the current
3881      * thread runs again.
3882      */
3883     static final void threadYield() {
3884 	// Note that we can't just use Thread.yield(), since it
3885 	// doesn't guarantee that it will actually yield the thread
3886 	// (and, in fact, it appears to be a no-op under Windows). So
3887 	// we will sleep for 1 msec instead. Since most threads use
3888 	// wait/notify, and only use this when they are waiting for
3889 	// another thread to finish something, this shouldn't be a
3890 	// performance concern.
3891 
3892 	//Thread.yield();
3893 	try {
3894 	    Thread.sleep(1);
3895 	}
3896 	catch (InterruptedException e) {
3897 	    // Do nothing, since we really don't care how long (or
3898 	    // even whether) we sleep
3899 	}
3900     }
3901 
3902     // Return the number of available processors
3903     private int getNumberOfProcessors() {
3904         return Runtime.getRuntime().availableProcessors();
3905     }
3906 
3907     //
3908     // The following framework supports code instrumentation. To use it,
3909     // add code of the following form to areas that you want to enable for
3910     // timing:
3911     //
3912     //     long startTime = System.nanoTime();
3913     //     sortTransformGroups(tSize, tgs);
3914     //     long deltaTime = System.nanoTime() - startTime;
3915     //     VirtualUniverse.mc.recordTime(MasterControl.TimeType.XXXXX, deltaTime);
3916     //
3917     // where "XXXXX" is the enum representing the code segment being timed.
3918     // Additional enums can be defined for new subsystems.
3919     //
3920 
3921     static enum TimeType {
3922         TOTAL_FRAME,
3923         RENDER,
3924         BEHAVIOR,
3925         // TRANSFORM_UPDATE,
3926         // ...
3927     }
3928 
3929     private long[] statTimes = new long[TimeType.values().length];
3930     private int[] statCounts = new int[TimeType.values().length];
3931     private boolean[] statSeen = new boolean[TimeType.values().length];
3932     private int frameCycleTick = 0;
3933     private long frameCycleNumber = 0L;
3934 
3935     // Method to record times -- should not be called unless the stats logger
3936     // level is set to INFO or lower
3937     synchronized void recordTime(TimeType type, long deltaTime) {
3938         int idx = type.ordinal();
3939         statTimes[idx] += deltaTime;
3940         statCounts[idx]++;
3941         statSeen[idx] = true;
3942     }
3943 
3944     // Method to record times -- this is not called unless the stats logger
3945     // level is set to INFO or lower
3946     private synchronized void logTimes() {
3947         ++frameCycleNumber;
3948         if (++frameCycleTick >= 10) {
3949             StringBuffer strBuf = new StringBuffer();
3950             strBuf.append("----------------------------------------------\n").
3951                     append("    Frame Number = ").
3952                     append(frameCycleNumber).
3953                     append("\n");
3954             for (int i = 0; i < statTimes.length; i++) {
3955                 if (statSeen[i]) {
3956                     strBuf.append("    ");
3957                     if (statCounts[i] > 0) {
3958                         strBuf.append(TimeType.values()[i]).
3959                                 append(" [").
3960                                 append(statCounts[i]).
3961                                 append("] = ").
3962                                 append((double)statTimes[i] / 1000000.0 / (double)statCounts[i]).
3963                                 append(" msec per call\n");
3964                         statTimes[i] = 0L;
3965                         statCounts[i] = 0;
3966                     } else {
3967                         assert statTimes[i] == 0L;
3968                         strBuf.append(TimeType.values()[i]).
3969                                 append(" [0] = 0.0 msec\n");
3970                     }
3971                 }
3972             }
3973             getStatsLogger().info(strBuf.toString());
3974             frameCycleTick = 0;
3975         }
3976     }
3977 
3978 }
3979