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