/*
* Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
/*
* Portions of this code were derived from work done by the Blackdown
* group (www.blackdown.org), who did the initial Linux implementation
* of the Java 3D API.
*/
package javax.media.j3d;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
class MasterControl {
/**
* Options for the runMonitor
*/
static final int CHECK_FOR_WORK = 0;
static final int SET_WORK = 1;
static final int RUN_THREADS = 2;
static final int THREAD_DONE = 3;
static final int SET_WORK_FOR_REQUEST_RENDERER = 5;
static final int RUN_RENDERER_CLEANUP = 6;
// The thread states for MC
static final int SLEEPING = 0;
static final int RUNNING = 1;
static final int WAITING_FOR_THREADS = 3;
static final int WAITING_FOR_CPU = 4;
static final int WAITING_FOR_RENDERER_CLEANUP = 5;
// Constants used in renderer thread argument
static final Integer REQUESTRENDER = new Integer(Renderer.REQUESTRENDER);
static final Integer RENDER = new Integer(Renderer.RENDER);
static final Integer SWAP = new Integer(Renderer.SWAP);
// Constants used for request from user threads
static final Integer ACTIVATE_VIEW = new Integer(1);
static final Integer DEACTIVATE_VIEW = new Integer(2);
static final Integer START_VIEW = new Integer(3);
static final Integer STOP_VIEW = new Integer(4);
static final Integer REEVALUATE_CANVAS = new Integer(5);
static final Integer UNREGISTER_VIEW = new Integer(6);
static final Integer PHYSICAL_ENV_CHANGE = new Integer(7);
static final Integer INPUTDEVICE_CHANGE = new Integer(8);
static final Integer EMPTY_UNIVERSE = new Integer(9);
static final Integer START_RENDERER = new Integer(10);
static final Integer STOP_RENDERER = new Integer(11);
static final Integer RENDER_ONCE = new Integer(12);
static final Integer FREE_CONTEXT = new Integer(13);
static final Integer FREE_DRAWING_SURFACE = new Integer(14);
static final Integer FREE_MESSAGE = new Integer(15);
static final Integer RESET_CANVAS = new Integer(16);
static final Integer GETBESTCONFIG = new Integer(17);
static final Integer ISCONFIGSUPPORT = new Integer(18);
static final Integer SET_GRAPHICSCONFIG_FEATURES = new Integer(19);
static final Integer SET_QUERYPROPERTIES = new Integer(20);
static final Integer SET_VIEW = new Integer(21);
// Developer logger for reporting informational messages; see getDevLogger()
private static boolean devLoggerEnabled = false;
private static Logger devLogger;
// Stats logger for reporting runtime statistics; see getStatsLogger()
private static boolean statsLoggerEnabled = false;
private static Logger statsLogger;
// Core logger for reporting internal errors, warning, and
// informational messages; see getCoreLogger()
private static boolean coreLoggerEnabled = false;
private static Logger coreLogger;
// Flag indicating that the rendering pipeline libraries are loaded
private static boolean librariesLoaded = false;
/**
* reference to MasterControl thread
*/
private MasterControlThread mcThread = null;
/**
* The list of views that are currently registered
*/
private UnorderList views = new UnorderList(1, View.class);
/**
* by MIK OF CLASSX
*
* the flag to indicate whether the background of the offscreen
* canvas must be transparent or not false by default
*/
boolean transparentOffScreen = false;
/**
* Flag to indicate whether Pbuffers are used for off-screen
* rendering; true by default. Set by the "j3d.usePbuffer"
* property, When this flag is set to false, Bitmap (Windows) or
* Pixmap (UNIX) rendering will be used
*/
boolean usePbuffer = true;
/**
* Flag to indicate whether should renderer view frustum culling is done;
* true by default.
* Set by the -Dj3d.viewFrustumCulling property, When this flag is
* set to false, the renderer view frustum culling is turned off.
*/
boolean viewFrustumCulling = true;
/**
* the flag to indicate whether the geometry should be locked or not
*/
private boolean lockGeometry = false;
/**
* The number of registered views that are active
*/
private int numActiveViews = 0;
/**
* The list of active universes get from View
*/
private UnorderList activeUniverseList = new UnorderList(VirtualUniverse.class);
/**
* The list of universes register from View
*/
private UnorderList regUniverseList = new UnorderList(VirtualUniverse.class);
/**
* A lock used for accessing time structures.
*/
private Object timeLock = new Object();
/**
* The current "time" value
*/
private long time = 0;
/**
* Use to assign threadOpts in Renderer thread.
*/
private long waitTimestamp = 0;
/**
* The current list of work threads
*/
private UnorderList stateWorkThreads =
new UnorderList(J3dThreadData.class);
private UnorderList renderWorkThreads =
new UnorderList(J3dThreadData.class);
private UnorderList requestRenderWorkThreads =
new UnorderList(J3dThreadData.class);
/**
* The current list of work threads
*/
private UnorderList renderThreadData = new UnorderList(J3dThreadData.class);
/**
* The list of input device scheduler thread
*/
private UnorderList inputDeviceThreads =
new UnorderList(1, InputDeviceScheduler.class);
/**
* A flag that is true when the thread lists need updating
*/
private boolean threadListsChanged;
/**
* Markers for the last transform structure update thread
* and the last update thread.
*/
private int lastTransformStructureThread = 0;
private int lastStructureUpdateThread = 0;
/**
* The current time snapshots
*/
private long currentTime;
// Only one Timer thread in the system.
TimerThread timerThread;
// Only one Notification thread in the system.
private NotificationThread notificationThread;
/**
* This flag indicates that MC is running
*/
volatile boolean running = true;
/**
* This flag indicates that MC has work to do
*/
private boolean workToDo = false;
/**
* This flag indicates that there is work for requestRenderer
*/
private boolean requestRenderWorkToDo = false;
/**
* The number of THREAD_DONE messages pending
*/
private int threadPending = 0;
private int renderPending = 0;
private int statePending = 0;
/**
* State variables for work lists
*/
private boolean renderWaiting = false;
private boolean stateWaiting = false;
/**
* The current state of the MC thread
*/
private int state = SLEEPING;
// time for sleep in order to met the minimum frame duration
private long sleepTime = 0;
/**
* The number of cpu's Java 3D may use
*/
private int cpuLimit;
/**
* A list of mirror objects to be updated
*/
private UnorderList mirrorObjects = new UnorderList(ObjectUpdate.class);
/**
* The renderingAttributesStructure for updating node component
* objects
*/
private RenderingAttributesStructure renderingAttributesStructure =
new RenderingAttributesStructure();
/**
* The default rendering method
*/
private DefaultRenderMethod defaultRenderMethod = null;
/**
* The text3D rendering method
*/
private Text3DRenderMethod text3DRenderMethod = null;
/**
* The vertex array rendering method
*/
private VertexArrayRenderMethod vertexArrayRenderMethod = null;
/**
* The displayList rendering method
*/
private DisplayListRenderMethod displayListRenderMethod = null;
/**
* The compressed geometry rendering method
*/
private CompressedGeometryRenderMethod compressedGeometryRenderMethod = null;
/**
* The oriented shape3D rendering method
*/
private OrientedShape3DRenderMethod orientedShape3DRenderMethod = null;
/**
* This is the start time upon which alpha's and behaviors
* are synchronized to. It is initialized once, the first time
* that a MasterControl object is created.
*/
static long systemStartTime = 0L;
// This is a time stamp used when context is created
private long contextTimeStamp = 0;
// This is an array of canvasIds in used
private boolean[] canvasIds = null;
private int canvasFreeIndex = 0;
private Object canvasIdLock = new Object();
// This is a counter for rendererBit
private int rendererCount = 0;
// Flag that indicates whether to shared display context or not
boolean isSharedCtx = false;
// Flag that tells us to use NV_register_combiners
boolean useCombiners = false;
// Flag that indicates whether compile is disabled or not
boolean disableCompile = false;
// Flag that indicates whether or not compaction occurs
boolean doCompaction = true;
// Flag that indicates whether separate specular color is disabled or not
boolean disableSeparateSpecularColor = false;
// Flag that indicates whether DisplayList is used or not
boolean isDisplayList = true;
// If this flag is set, then by-ref geometry will not be
// put in display list
boolean buildDisplayListIfPossible = false;
// If this flag is set, then geometry arrays with vertex attributes can
// be in display list.
boolean vertexAttrsInDisplayList = false;
// Issue 249 - flag that indicates whether the soleUser optimization is permitted
boolean allowSoleUser = false;
// Issue 266 - Flag indicating whether null graphics configs are allowed
// Set by -Dj3d.allowNullGraphicsConfig property
// Setting this flag causes Canvas3D to allow a null GraphicsConfiguration
// for on-screen canvases. This is only for backward compatibility with
// legacy applications.
boolean allowNullGraphicsConfig = false;
// Issue 239 - Flag indicating whether the stencil buffer is cleared by
// default each frame when the color and depth buffers are cleared.
// Note that this is a partial solution, since we eventually want an API
// to control this.
boolean stencilClear = false;
// REQUESTCLEANUP messages argument
static Integer REMOVEALLCTXS_CLEANUP = new Integer(1);
static Integer REMOVECTX_CLEANUP = new Integer(2);
static Integer REMOVENOTIFY_CLEANUP = new Integer(3);
static Integer RESETCANVAS_CLEANUP = new Integer(4);
static Integer FREECONTEXT_CLEANUP = new Integer(5);
// arguments for renderer resource cleanup run
Object rendererCleanupArgs[] = {new Integer(Renderer.REQUESTCLEANUP),
null, null};
// Context creation should obtain this lock, so that
// first_time and all the extension initilialization
// are done in the MT safe manner
Object contextCreationLock = new Object();
// Flag that indicates whether to lock the DSI while rendering
boolean doDsiRenderLock = false;
// Flag that indicates the pre-1.5 behavior of enforcing power-of-two
// textures. If set, then any non-power-of-two textures will throw an
// exception.
boolean enforcePowerOfTwo = false;
// Flag that indicates whether the framebuffer is sharing the
// Z-buffer with both the left and right eyes when in stereo mode.
// If this is true, we need to clear the Z-buffer between rendering
// to the left and right eyes.
boolean sharedStereoZBuffer = true;
// True to disable all underlying multisampling API so it uses
// the setting in the driver.
boolean implicitAntialiasing = false;
// False to disable compiled vertex array extensions if support
boolean isCompiledVertexArray = true;
// Number of reserved vertex attribute locations for GLSL (must be at
// least 1).
// Issue 269 - need to reserve up to 6 vertex attribtue locations to ensure
// that we don't collide with a predefined gl_* attribute on nVidia cards.
int glslVertexAttrOffset = 6;
// Hashtable that maps a GraphicsDevice to its associated
// Screen3D--this is only used for on-screen Canvas3Ds
Hashtable<GraphicsDevice, Screen3D> deviceScreenMap = new Hashtable<GraphicsDevice, Screen3D>();
// Use to store all requests from user threads.
UnorderList requestObjList = new UnorderList();
private UnorderList requestTypeList = new UnorderList(Integer.class);
// Temporary storage to store stop request for requestViewList
private UnorderList tempViewList = new UnorderList();
private UnorderList renderOnceList = new UnorderList();
// This flag is true when there is pending request
// i.e. false when the above requestxxx Lists are all empty.
private boolean pendingRequest = false;
// Root ThreadGroup for creating Java 3D threads
private static ThreadGroup rootThreadGroup;
// Thread priority for all Java 3D threads
private static int threadPriority;
static private Object mcThreadLock = new Object();
private ArrayList<View> timestampUpdateList = new ArrayList<View>(3);
private UnorderList freeMessageList = new UnorderList(8);
// Maximum number of lights
int maxLights;
// Set by the -Dj3d.sortShape3DBounds property, When this flag is
// set to true, the bounds of the Shape3D node will be used in
// place of the computed GeometryArray bounds for transparency
// sorting for those Shape3D nodes whose boundsAutoCompute
// attribute is set to false.
boolean sortShape3DBounds = false;
//Set by -Dj3d.forceReleaseView property.
//Setting this flag as true disables the bug fix 4267395 in View deactivate().
//The bug 4267395 can lock-up *some* systems, but the bug fix can
//produce memory leaks in applications which creates and destroy Canvas3D
//from time to time.
//Set as true if you have memory leaks after disposing Canvas3D.
//Default false value does affect Java3D View dispose behavior.
boolean forceReleaseView = false;
// Issue 480: Cache the bounds of nodes so that getBounds does not
// recompute the boounds of the entire graph per call
boolean cacheAutoComputedBounds = false;
// issue 544
boolean useBoxForGroupBounds = false;
/**
* Constructs a new MasterControl object. Note that there is
* exatly one MasterControl object, created statically by
* VirtualUniverse.
*/
MasterControl() {
assert librariesLoaded;
// Initialize the start time upon which alpha's and behaviors
// are synchronized to (if it isn't already set).
if (systemStartTime == 0L) {
systemStartTime = J3dClock.currentTimeMillis();
}
if(J3dDebug.devPhase) {
// Check to see whether debug mode is allowed
J3dDebug.debug = getBooleanProperty("j3d.debug", false,
"J3dDebug.debug");
}
// Check to see whether shared contexts are allowed
isSharedCtx = getBooleanProperty("j3d.sharedctx", isSharedCtx, "shared contexts");
doCompaction = getBooleanProperty("j3d.docompaction", doCompaction,
"compaction");
// by MIK OF CLASSX
transparentOffScreen = getBooleanProperty("j3d.transparentOffScreen", transparentOffScreen, "transparent OffScreen");
usePbuffer = getBooleanProperty("j3d.usePbuffer",
usePbuffer,
"Off-screen Pbuffer");
viewFrustumCulling = getBooleanProperty("j3d.viewFrustumCulling", viewFrustumCulling,"View frustum culling in the renderer is");
sortShape3DBounds =
getBooleanProperty("j3d.sortShape3DBounds", sortShape3DBounds,
"Shape3D bounds enabled for transparency sorting",
"Shape3D bounds *ignored* for transparency sorting");
forceReleaseView =
getBooleanProperty("j3d.forceReleaseView", forceReleaseView,
"forceReleaseView after Canvas3D dispose enabled",
"forceReleaseView after Canvas3D dispose disabled");
// FIXME: GL_NV_register_combiners
// useCombiners = getBooleanProperty("j3d.usecombiners", useCombiners,
// "Using NV_register_combiners if available",
// "NV_register_combiners disabled");
if (getProperty("j3d.disablecompile") != null) {
disableCompile = true;
System.err.println("Java 3D: BranchGroup.compile disabled");
}
if (getProperty("j3d.disableSeparateSpecular") != null) {
disableSeparateSpecularColor = true;
System.err.println("Java 3D: separate specular color disabled if possible");
}
isDisplayList = getBooleanProperty("j3d.displaylist", isDisplayList,
"display list");
implicitAntialiasing =
getBooleanProperty("j3d.implicitAntialiasing",
implicitAntialiasing,
"implicit antialiasing");
isCompiledVertexArray =
getBooleanProperty("j3d.compiledVertexArray",
isCompiledVertexArray,
"compiled vertex array");
boolean j3dOptimizeSpace =
getBooleanProperty("j3d.optimizeForSpace", true,
"optimize for space");
if (isDisplayList) {
// Build Display list for by-ref geometry
// ONLY IF optimizeForSpace is false
if (!j3dOptimizeSpace) {
buildDisplayListIfPossible = true;
}
// Build display lists for geometry with vertex attributes
// ONLY if we are in GLSL mode and GLSL shaders are available
vertexAttrsInDisplayList = true;
}
// Check to see whether Renderer can run without DSI lock
doDsiRenderLock = getBooleanProperty("j3d.renderLock",
doDsiRenderLock,
"render lock");
// Check to see whether we enforce power-of-two textures
enforcePowerOfTwo = getBooleanProperty("j3d.textureEnforcePowerOfTwo",
enforcePowerOfTwo,
"checking power-of-two textures");
// Issue 249 - check to see whether the soleUser optimization is permitted
allowSoleUser = getBooleanProperty("j3d.allowSoleUser",
allowSoleUser,
"sole-user mode");
// Issue 266 - check to see whether null graphics configs are allowed
allowNullGraphicsConfig = getBooleanProperty("j3d.allowNullGraphicsConfig",
allowNullGraphicsConfig,
"null graphics configs");
// Issue 239 - check to see whether per-frame stencil clear is enabled
stencilClear = getBooleanProperty("j3d.stencilClear",
stencilClear,
"per-frame stencil clear");
// Check to see if stereo mode is sharing the Z-buffer for both eyes.
sharedStereoZBuffer =
getBooleanProperty("j3d.sharedstereozbuffer",
sharedStereoZBuffer,
"shared stereo Z buffer");
// Get the maximum number of concurrent threads (CPUs)
final int defaultThreadLimit = getNumberOfProcessors() + 1;
Integer threadLimit = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Integer>() {
@Override
public Integer run() {
return Integer.getInteger("j3d.threadLimit", defaultThreadLimit);
}
});
cpuLimit = threadLimit.intValue();
if (cpuLimit < 1)
cpuLimit = 1;
if (J3dDebug.debug || cpuLimit != defaultThreadLimit) {
System.err.println("Java 3D: concurrent threadLimit = " +
cpuLimit);
}
// Get the input device scheduler sampling time
Integer samplingTime = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Integer>() {
@Override
public Integer run() {
return Integer.getInteger("j3d.deviceSampleTime", 0);
}
});
if (samplingTime.intValue() > 0) {
InputDeviceScheduler.samplingTime =
samplingTime.intValue();
System.err.println("Java 3D: Input device sampling time = "
+ samplingTime + " ms");
}
// Get the glslVertexAttrOffset
final int defaultGLSLVertexAttrOffset = glslVertexAttrOffset;
Integer vattrOffset = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Integer>() {
@Override
public Integer run() {
return Integer.getInteger("j3d.glslVertexAttrOffset",
defaultGLSLVertexAttrOffset);
}
});
glslVertexAttrOffset = vattrOffset.intValue();
if (glslVertexAttrOffset < 1) {
glslVertexAttrOffset = 1;
}
if (J3dDebug.debug || glslVertexAttrOffset != defaultGLSLVertexAttrOffset) {
System.err.println("Java 3D: glslVertexAttrOffset = " +
glslVertexAttrOffset);
}
// Issue 480 : Cache bounds returned by getBounds()
cacheAutoComputedBounds =
getBooleanProperty("j3d.cacheAutoComputeBounds",
cacheAutoComputedBounds,
"Cache AutoCompute Bounds, accelerates getBounds()");
// Issue 544
useBoxForGroupBounds =
getBooleanProperty("j3d.useBoxForGroupBounds",
useBoxForGroupBounds,
"Use of BoundingBox for group geometric bounds");
// Check for obsolete properties
String[] obsoleteProps = {
"j3d.backgroundtexture",
"j3d.forceNormalized",
"j3d.g2ddrawpixel",
"j3d.simulatedMultiTexture",
"j3d.useFreeLists",
};
for (int i = 0; i < obsoleteProps.length; i++) {
if (getProperty(obsoleteProps[i]) != null) {
System.err.println("Java 3D: " + obsoleteProps[i] + " property ignored");
}
}
// Get the maximum Lights
maxLights = Pipeline.getPipeline().getMaximumLights();
// create the freelists
FreeListManager.createFreeLists();
// create an array canvas use registers
// The 32 limit can be lifted once the
// resourceXXXMasks in other classes
// are change not to use integer.
canvasIds = new boolean[32];
for(int i=0; i<canvasIds.length; i++) {
canvasIds[i] = false;
}
canvasFreeIndex = 0;
}
private static boolean initLogger(Logger logger, Level defaultLevel) {
if (logger == null) {
return false;
}
if (defaultLevel != null &&
logger.getLevel() == null &&
Logger.getLogger("j3d").getLevel() == null) {
try {
// Set default logger level rather than inheriting from system global
logger.setLevel(defaultLevel);
} catch (SecurityException ex) {
System.err.println(ex);
return false;
}
}
return logger.isLoggable(Level.SEVERE);
}
// Called by the static initializer to initialize the loggers
private static void initLoggers() {
coreLogger = Logger.getLogger("j3d.core");
devLogger = Logger.getLogger("j3d.developer");
statsLogger = Logger.getLogger("j3d.stats");
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
coreLoggerEnabled = initLogger(coreLogger, null);
devLoggerEnabled = initLogger(devLogger, Level.OFF);
statsLoggerEnabled = initLogger(statsLogger, Level.OFF);
return null;
}
});
}
/**
* Get the developer logger -- OFF by default
*
* WARNING - for probable incorrect or inconsistent api usage
* INFO - for informational messages such as performance hints (less verbose than FINE)
* FINE - for informational messages from inner loops
* FINER - using default values which may not be optimal
*/
static Logger getDevLogger() {
return devLogger;
}
static boolean isDevLoggable(Level level) {
return devLoggerEnabled && devLogger.isLoggable(level);
}
/**
* Get the stats logger -- OFF by default
*
* WARNING - statistical anomalies
* INFO - basic performance stats - not too verbose and minimally intrusive
* FINE - somewhat verbose and intrusive
* FINER - more verbose and intrusive
* FINEST - most verbose and intrusive
*/
static Logger getStatsLogger() {
return statsLogger;
}
static boolean isStatsLoggable(Level level) {
return statsLoggerEnabled && statsLogger.isLoggable(level);
}
/**
* Get the core logger -- level is INFO by default
*
* SEVERE - Serious internal errors
* WARNING - Possible internal errors or anomalies
* INFO - General informational messages
* FINE - Internal debugging information - somewhat verbose
* FINER - Internal debugging information - more verbose
* FINEST - Internal debugging information - most verbose
*/
static Logger getCoreLogger() {
return coreLogger;
}
static boolean isCoreLoggable(Level level) {
return coreLoggerEnabled && coreLogger.isLoggable(level);
}
private static String getProperty(final String prop) {
return java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<String>() {
@Override
public String run() {
return System.getProperty(prop);
}
});
}
static boolean getBooleanProperty(String prop,
boolean defaultValue,
String trueMsg,
String falseMsg) {
boolean value = defaultValue;
String propValue = getProperty(prop);
if (propValue != null) {
value = Boolean.valueOf(propValue).booleanValue();
if (J3dDebug.debug)
System.err.println("Java 3D: " + (value ? trueMsg : falseMsg));
}
return value;
}
static boolean getBooleanProperty(String prop,
boolean defaultValue,
String msg) {
return getBooleanProperty(prop,
defaultValue,
(msg + " enabled"),
(msg + " disabled"));
}
/**
* Method to create and initialize the rendering Pipeline object,
* and to load the native libraries needed by Java 3D. This is
* called by the static initializer in VirtualUniverse <i>before</i>
* the MasterControl object is created.
*/
static void loadLibraries() {
assert !librariesLoaded;
// Initialize the Pipeline object associated with the
// renderer specified by the "j3d.rend" system property.
//
// XXXX : We should consider adding support for a more flexible,
// dynamic selection scheme via an API call.
// Default rendering pipeline is the JOGL pipeline
Pipeline.Type pipelineType = Pipeline.Type.JOGL;
final String rendStr = getProperty("j3d.rend");
if (rendStr == null) {
// Use default pipeline
} else if (rendStr.equals("jogl")) {
pipelineType = Pipeline.Type.JOGL;
} else if (rendStr.equals("noop")) {
pipelineType = Pipeline.Type.NOOP;
} else {
System.err.println("Java 3D: Unrecognized renderer: " + rendStr);
// Use default pipeline
}
// Construct the singleton Pipeline instance
Pipeline.createPipeline(pipelineType);
librariesLoaded = true;
}
/**
* Invoke from InputDeviceScheduler to create an
* InputDeviceBlockingThread.
*/
InputDeviceBlockingThread getInputDeviceBlockingThread(
final InputDevice device) {
return java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<InputDeviceBlockingThread>() {
@Override
public InputDeviceBlockingThread run() {
synchronized (rootThreadGroup) {
InputDeviceBlockingThread thread = new InputDeviceBlockingThread(
rootThreadGroup, device);
thread.setPriority(threadPriority);
return thread;
}
}
});
}
/**
* Set thread priority to all threads under Java3D thread group.
*/
void setThreadPriority(final int pri) {
synchronized (rootThreadGroup) {
threadPriority = pri;
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
Thread list[] = new
Thread[rootThreadGroup.activeCount()];
int count = rootThreadGroup.enumerate(list);
for (int i=count-1; i >=0; i--) {
list[i].setPriority(pri);
}
return null;
}
});
}
}
/**
* Return Java3D thread priority
*/
int getThreadPriority() {
return threadPriority;
}
/**
* This returns the a unused renderer bit
*/
int getRendererBit() {
return (1 << rendererCount++);
}
/**
* This returns the a unused renderer bit
*/
int getRendererId() {
return rendererCount++;
}
/**
* This returns a context creation time stamp
* Note: this has to be called under the contextCreationLock
*/
long getContextTimeStamp() {
return (++contextTimeStamp);
}
/**
* This returns the a unused displayListId
*/
Integer getDisplayListId() {
return (Integer) FreeListManager.getObject(FreeListManager.DISPLAYLIST);
}
void freeDisplayListId(Integer id) {
FreeListManager.freeObject(FreeListManager.DISPLAYLIST, id);
}
int getCanvasId() {
int i;
synchronized(canvasIdLock) {
// Master control need to keep count itself
for(i=canvasFreeIndex; i<canvasIds.length; i++) {
if(canvasIds[i] == false)
break;
}
if (i >= canvasIds.length) {
throw new RuntimeException("Cannot render to more than 32 Canvas3Ds");
}
canvasIds[i] = true;
canvasFreeIndex = i + 1;
}
return i;
}
void freeCanvasId(int canvasId) {
// Valid range is [0, 31]
synchronized(canvasIdLock) {
canvasIds[canvasId] = false;
if(canvasFreeIndex > canvasId) {
canvasFreeIndex = canvasId;
}
}
}
/**
* Create a Renderer if it is not already done so.
* This is used for GraphicsConfigTemplate3D passing
* graphics call to RequestRenderer, and for creating
* an off-screen buffer for an off-screen Canvas3D.
*/
private Renderer createRenderer(GraphicsConfiguration gc) {
final GraphicsDevice gd = gc.getDevice();
Renderer rdr = Screen3D.deviceRendererMap.get(gd);
if (rdr != null) {
return rdr;
}
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
Renderer r;
synchronized (rootThreadGroup) {
r = new Renderer(rootThreadGroup);
r.initialize();
r.setPriority(threadPriority);
Screen3D.deviceRendererMap.put(gd, r);
}
return null;
}
});
threadListsChanged = true;
return Screen3D.deviceRendererMap.get(gd);
}
/**
* Post the request in queue
*/
void postRequest(Integer type, Object obj) {
synchronized (mcThreadLock) {
synchronized (requestObjList) {
if (mcThread == null) {
if ((type == ACTIVATE_VIEW) ||
(type == GETBESTCONFIG) ||
(type == SET_VIEW) ||
(type == ISCONFIGSUPPORT) ||
(type == SET_QUERYPROPERTIES) ||
(type == SET_GRAPHICSCONFIG_FEATURES)) {
createMasterControlThread();
requestObjList.add(obj);
requestTypeList.add(type);
pendingRequest = true;
} else if (type == EMPTY_UNIVERSE) {
destroyUniverseThreads((VirtualUniverse) obj);
} else if (type == STOP_VIEW) {
View v = (View) obj;
v.stopViewCount = -1;
v.isRunning = false;
} else if (type == STOP_RENDERER) {
if (obj instanceof Canvas3D) {
((Canvas3D) obj).isRunningStatus = false;
} else {
((Renderer) obj).userStop = true;
}
} else if (type == UNREGISTER_VIEW) {
((View) obj).doneUnregister = true;
} else {
requestObjList.add(obj);
requestTypeList.add(type);
pendingRequest = true;
}
} else {
requestObjList.add(obj);
requestTypeList.add(type);
pendingRequest = true;
}
}
}
setWork();
}
/**
* This procedure is invoked when isRunning is false.
* Return true when there is no more pending request so that
* Thread can terminate. Otherwise we have to recreate
* the MC related threads.
*/
boolean mcThreadDone() {
synchronized (mcThreadLock) {
synchronized (requestObjList) {
if (!pendingRequest) {
mcThread = null;
if (renderingAttributesStructure.updateThread !=
null) {
renderingAttributesStructure.updateThread.finish();
renderingAttributesStructure.updateThread =
null;
}
renderingAttributesStructure = new RenderingAttributesStructure();
if (timerThread != null) {
timerThread.finish();
timerThread = null;
}
if (notificationThread != null) {
notificationThread.finish();
notificationThread = null;
}
requestObjList.clear();
requestTypeList.clear();
return true;
}
running = true;
createMCThreads();
return false;
}
}
}
/**
* This method increments and returns the next time value
* timeLock must get before this procedure is invoked
*/
final long getTime() {
return (time++);
}
/**
* This takes a given message and parses it out to the structures and
* marks its time value.
*/
void processMessage(J3dMessage message) {
synchronized (timeLock) {
message.time = getTime();
sendMessage(message);
}
setWork();
}
/**
* This takes an array of messages and parses them out to the structures and
* marks the time value. Make sure, setWork() is done at the very end
* to make sure all the messages will be processed in the same frame
*/
void processMessage(J3dMessage[] messages) {
synchronized (timeLock) {
long time = getTime();
for (int i = 0; i < messages.length; i++) {
messages[i].time = time;
sendMessage(messages[i]);
}
}
setWork();
}
/**
* This takes the specified notification message and sends it to the
* notification thread for processing.
*/
void sendNotification(J3dNotification notification) {
notificationThread.addNotification(notification);
}
/**
* Create and start the MasterControl Thread.
*/
void createMasterControlThread() {
// Issue 364: don't create master control thread if already created
if (mcThread != null) {
return;
}
running = true;
workToDo = true;
state = RUNNING;
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
mcThread = new
MasterControlThread(rootThreadGroup);
mcThread.setPriority(threadPriority);
}
return null;
}
});
}
// assuming the timeLock is already acquired
/**
* Send a message to another Java 3D thread.
*/
void sendMessage(J3dMessage message) {
synchronized (message) {
VirtualUniverse u = message.universe;
int targetThreads = message.threads;
if (isCoreLoggable(Level.FINEST)) {
dumpMessage("sendMessage", message);
}
if ((targetThreads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
renderingAttributesStructure.addMessage(message);
}
// GraphicsContext3D send message with universe = null
if (u != null) {
if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
u.geometryStructure.addMessage(message);
}
if ((targetThreads & J3dThread.UPDATE_TRANSFORM) != 0) {
u.transformStructure.addMessage(message);
}
if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
u.behaviorStructure.addMessage(message);
}
if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
u.soundStructure.addMessage(message);
}
if ((targetThreads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
u.renderingEnvironmentStructure.addMessage(message);
}
}
if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
// Note that we don't check for active view
if (message.view != null && message.view.soundScheduler != null ) {
// This make sure that message won't lost even
// though this view not yet register
message.view.soundScheduler.addMessage(message);
} else {
synchronized (views) {
View v[] = (View []) views.toArray(false);
int i = views.arraySize()-1;
if (u == null) {
while (i>=0) {
v[i--].soundScheduler.addMessage(message);
}
} else {
while (i>=0) {
if (v[i].universe == u) {
v[i].soundScheduler.addMessage(message);
}
i--;
}
}
}
}
}
if ((targetThreads & J3dThread.UPDATE_RENDER) != 0) {
// Note that we don't check for active view
if (message.view != null && message.view.renderBin != null) {
// This make sure that message won't lost even
// though this view not yet register
message.view.renderBin.addMessage(message);
} else {
synchronized (views) {
View v[] = (View []) views.toArray(false);
int i = views.arraySize()-1;
if (u == null) {
while (i>=0) {
v[i--].renderBin.addMessage(message);
}
}
else {
while (i>=0) {
if (v[i].universe == u) {
v[i].renderBin.addMessage(message);
}
i--;
}
}
}
}
}
if (message.getRefcount() == 0) {
message.clear();
}
}
}
/**
* Send a message to another Java 3D thread.
* This variant is only call by TimerThread for Input Device Scheduler
* or to redraw all View for RenderThread
*/
void sendRunMessage(int targetThreads) {
synchronized (timeLock) {
long time = getTime();
if ((targetThreads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
synchronized (inputDeviceThreads) {
InputDeviceScheduler ds[] = (InputDeviceScheduler [])
inputDeviceThreads.toArray(false);
for (int i=inputDeviceThreads.size()-1; i >=0; i--) {
if (ds[i].physicalEnv.activeViewRef > 0) {
ds[i].getThreadData().lastUpdateTime =
time;
}
}
// timerThread instance in MC will set to null in
// destroyUniverseThreads() so we need to check if
// TimerThread kick in to sendRunMessage() after that.
// It happens because TimerThread is the only thread run
// asychronizously with MasterControl thread.
if (timerThread != null) {
// Notify TimerThread to wakeup this procedure
// again next time.
timerThread.addInputDeviceSchedCond();
}
}
}
if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
synchronized (renderThreadData) {
J3dThreadData[] threads = (J3dThreadData [])
renderThreadData.toArray(false);
int i=renderThreadData.arraySize()-1;
J3dThreadData thr;
while (i>=0) {
thr = threads[i--];
if ( thr.view.renderBinReady) {
thr.lastUpdateTime = time;
}
}
}
}
}
setWork();
}
/**
* Send a message to another Java 3D thread.
* This variant is only call by TimerThread for Sound Scheduler
*/
void sendRunMessage(long waitTime, View view, int targetThreads) {
synchronized (timeLock) {
long time = getTime();
if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
if (view.soundScheduler != null) {
view.soundScheduler.threadData.lastUpdateTime = time;
}
// wakeup this procedure next time
// QUESTION: waitTime calculated some milliseconds BEFORE
// this methods getTime() called - shouldn't actual
// sound Complete time be passed by SoundScheduler
// QUESTION: will this wake up only soundScheduler associated
// with this view?? (since only it's lastUpdateTime is set)
// or all soundSchedulers??
timerThread.addSoundSchedCond(time+waitTime);
}
}
setWork();
}
/**
* Send a message to another Java 3D thread.
* This variant is only called to update Render Thread
*/
void sendRunMessage(View v, int targetThreads) {
synchronized (timeLock) {
long time = getTime();
if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
synchronized (renderThreadData) {
J3dThreadData[] threads = (J3dThreadData [])
renderThreadData.toArray(false);
int i=renderThreadData.arraySize()-1;
J3dThreadData thr;
while (i>=0) {
thr = threads[i--];
if (thr.view == v && v.renderBinReady) {
thr.lastUpdateTime = time;
}
}
}
}
}
setWork();
}
/**
* This sends a run message to the given threads.
*/
void sendRunMessage(VirtualUniverse u, int targetThreads) {
// We don't sendRunMessage to update structure except Behavior
synchronized (timeLock) {
long time = getTime();
if ((targetThreads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
if (u.behaviorScheduler != null) {
u.behaviorScheduler.getThreadData(null,
null).lastUpdateTime = time;
}
}
if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
u.behaviorStructure.threadData.lastUpdateTime = time;
}
if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
u.geometryStructure.threadData.lastUpdateTime = time;
}
if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
u.soundStructure.threadData.lastUpdateTime = time;
}
if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
synchronized (views) {
View v[] = (View []) views.toArray(false);
for (int i= views.arraySize()-1; i >=0; i--) {
if ((v[i].soundScheduler != null) &&
(v[i].universe == u)) {
v[i].soundScheduler.threadData.lastUpdateTime = time;
}
}
}
}
if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
synchronized (renderThreadData) {
J3dThreadData[] threads = (J3dThreadData [])
renderThreadData.toArray(false);
int i=renderThreadData.arraySize()-1;
J3dThreadData thr;
while (i>=0) {
thr = threads[i--];
if (thr.view.universe == u && thr.view.renderBinReady) {
thr.lastUpdateTime = time;
}
}
}
}
}
setWork();
}
/**
* Return a clone of View, we can't access
* individual element of View after getting the size
* in separate API call without synchronized views.
*/
UnorderList cloneView() {
return (UnorderList) views.clone();
}
/**
* Return true if view is already registered with MC
*/
boolean isRegistered(View view) {
return views.contains(view);
}
/**
* This snapshots the time values to be used for this iteration.
* Note that this method is called without the timeLock held.
* We must synchronize on timeLock to prevent updating
* thread.lastUpdateTime from user thread in sendMessage()
* or sendRunMessage().
*/
private void updateTimeValues() {
synchronized (timeLock) {
int i=0;
J3dThreadData lastThread=null;
J3dThreadData thread=null;
long lastTime = currentTime;
currentTime = getTime();
J3dThreadData threads[] = (J3dThreadData [])
stateWorkThreads.toArray(false);
int size = stateWorkThreads.arraySize();
while (i<lastTransformStructureThread) {
thread = threads[i++];
if ((thread.lastUpdateTime > thread.lastRunTime) &&
!thread.thread.userStop) {
lastThread = thread;
thread.needsRun = true;
thread.threadOpts = J3dThreadData.CONT_THREAD;
thread.lastRunTime = currentTime;
} else {
thread.needsRun = false;
}
}
if (lastThread != null) {
lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
lastThread = null;
}
while (i<lastStructureUpdateThread) {
thread = threads[i++];
if ((thread.lastUpdateTime > thread.lastRunTime) &&
!thread.thread.userStop) {
lastThread = thread;
thread.needsRun = true;
thread.threadOpts = J3dThreadData.CONT_THREAD;
thread.lastRunTime = currentTime;
} else {
thread.needsRun = false;
}
}
if (lastThread != null) {
lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
lastThread = null;
}
while (i<size) {
thread = threads[i++];
if ((thread.lastUpdateTime > thread.lastRunTime) &&
!thread.thread.userStop) {
lastThread = thread;
thread.needsRun = true;
thread.threadOpts = J3dThreadData.CONT_THREAD;
thread.lastRunTime = currentTime;
} else {
thread.needsRun = false;
}
}
if (lastThread != null) {
lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
lastThread = null;
}
threads = (J3dThreadData []) renderWorkThreads.toArray(false);
size = renderWorkThreads.arraySize();
View v;
J3dThreadData lastRunThread = null;
waitTimestamp++;
sleepTime = 0L;
boolean threadToRun = false; // Not currently used
// Fix for Issue 12: loop through the list of threads, calling
// computeCycleTime() exactly once per view. This ensures that
// all threads for a given view see consistent values for
// isMinCycleTimeAchieve and sleepTime.
v = null;
for (i=0; i<size; i++) {
thread = threads[i];
if (thread.view != v) {
thread.view.computeCycleTime();
// Set sleepTime to the value needed to satify the
// minimum cycle time of the slowest view
if (thread.view.sleepTime > sleepTime) {
sleepTime = thread.view.sleepTime;
}
}
v = thread.view;
}
v = null;
for (i=0; i<size; i++) {
thread = threads[i];
if (thread.canvas == null) { // Only for swap thread
((Object []) thread.threadArgs)[3] = null;
}
if ((thread.lastUpdateTime > thread.lastRunTime) &&
!thread.thread.userStop) {
if (thread.thread.lastWaitTimestamp == waitTimestamp) {
// This renderer thread is repeated. We must wait
// until all previous renderer threads done before
// allowing this thread to continue. Note that
// lastRunThread can't be null in this case.
waitTimestamp++;
if (thread.view != v) {
// A new View is start
v = thread.view;
threadToRun = true;
lastRunThread.threadOpts =
(J3dThreadData.STOP_TIMER |
J3dThreadData.WAIT_ALL_THREADS);
((Object []) lastRunThread.threadArgs)[3] = lastRunThread.view;
thread.threadOpts = (J3dThreadData.START_TIMER |
J3dThreadData.CONT_THREAD);
} else {
if ((lastRunThread.threadOpts &
J3dThreadData.START_TIMER) != 0) {
lastRunThread.threadOpts =
(J3dThreadData.START_TIMER |
J3dThreadData.WAIT_ALL_THREADS);
} else {
lastRunThread.threadOpts =
J3dThreadData.WAIT_ALL_THREADS;
}
thread.threadOpts = J3dThreadData.CONT_THREAD;
}
} else {
if (thread.view != v) {
v = thread.view;
threadToRun = true;
// Although the renderer thread is not
// repeated. We still need to wait all
// previous renderer threads if new View
// start.
if (lastRunThread != null) {
lastRunThread.threadOpts =
(J3dThreadData.STOP_TIMER |
J3dThreadData.WAIT_ALL_THREADS);
((Object []) lastRunThread.threadArgs)[3]
= lastRunThread.view;
}
thread.threadOpts = (J3dThreadData.START_TIMER |
J3dThreadData.CONT_THREAD);
} else {
thread.threadOpts = J3dThreadData.CONT_THREAD;
}
}
thread.thread.lastWaitTimestamp = waitTimestamp;
thread.needsRun = true;
thread.lastRunTime = currentTime;
lastRunThread = thread;
} else {
thread.needsRun = false;
}
}
if (lastRunThread != null) {
lastRunThread.threadOpts =
(J3dThreadData.STOP_TIMER |
J3dThreadData.WAIT_ALL_THREADS|
J3dThreadData.LAST_STOP_TIMER);
lockGeometry = true;
((Object []) lastRunThread.threadArgs)[3] = lastRunThread.view;
} else {
lockGeometry = false;
}
}
// Issue 275 - go to sleep without holding timeLock
// Sleep for the amount of time needed to satisfy the minimum
// cycle time for all views.
if (sleepTime > 0) {
// System.err.println("MasterControl: sleep(" + sleepTime + ")");
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
System.err.println(e);
}
// System.err.println("MasterControl: done sleeping");
}
}
private void createUpdateThread(J3dStructure structure) {
final J3dStructure s = structure;
if (s.updateThread == null) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
s.updateThread = new StructureUpdateThread(
rootThreadGroup, s, s.threadType);
s.updateThread.setPriority(threadPriority);
}
return null;
}
});
s.updateThread.initialize();
s.threadData.thread = s.updateThread;
// This takes into accout for thread that just destroy and
// create again. In this case the threadData may receive
// message before the thread actually created. We don't want
// the currentTime to overwrite the update time of which
// is set by threadData when get message.
s.threadData.lastUpdateTime = Math.max(currentTime,
s.threadData.lastUpdateTime);
}
}
private void emptyMessageList(J3dStructure structure, View v) {
if (structure != null) {
if (v == null) {
if (structure.threadData != null) {
structure.threadData.thread = null;
}
if (structure.updateThread != null) {
structure.updateThread.structure = null;
}
structure.updateThread = null;
}
boolean otherViewExist = false;
if ((v != null) && (v.universe != null)) {
// Check if there is any other View register with the
// same universe
for (int i=views.size()-1; i >= 0; i--) {
if (((View) views.get(i)).universe == v.universe) {
otherViewExist = true;
break;
}
}
}
UnorderList mlist = structure.messageList;
// Note that message is add at the end of array
synchronized (mlist) {
int size = mlist.size();
if (size > 0) {
J3dMessage mess[] = (J3dMessage []) mlist.toArray(false);
J3dMessage m;
int i = 0;
while (i < size) {
m = mess[i];
if ((v == null) || (m.view == v) ||
((m.view == null) && !otherViewExist)) {
if (m.type == J3dMessage.INSERT_NODES) {
// There is another View register request
// immediately following, so no need
// to remove message.
break;
}
// Some other thread may still using this
// message so we should not directly
// add this message to free lists
m.decRefcount();
mlist.removeOrdered(i);
size--;
} else {
i++;
}
}
}
}
}
}
private void destroyUpdateThread(J3dStructure structure) {
// If unregisterView message got before EMPTY_UNIVERSE
// message, then updateThread is already set to null.
if (structure.updateThread != null) {
structure.updateThread.finish();
structure.updateThread.structure = null;
structure.updateThread = null;
}
structure.threadData.thread = null;
structure.clearMessages();
}
/**
* This register a View with MasterControl.
* The View has at least one Canvas3D added to a container.
*/
private void registerView(View v) {
final VirtualUniverse univ = v.universe;
if (views.contains(v) && regUniverseList.contains(univ)) {
return; // already register
}
if (timerThread == null) {
// This handle the case when MC shutdown and restart in
// a series of pending request
running = true;
createMCThreads();
}
// If viewId is null, assign one ..
v.assignViewId();
// Create thread if not done before
createUpdateThread(univ.behaviorStructure);
createUpdateThread(univ.geometryStructure);
createUpdateThread(univ.soundStructure);
createUpdateThread(univ.renderingEnvironmentStructure);
createUpdateThread(univ.transformStructure);
// create Behavior scheduler
J3dThreadData threadData = null;
if (univ.behaviorScheduler == null) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
univ.behaviorScheduler = new BehaviorScheduler(
rootThreadGroup, univ);
univ.behaviorScheduler.setPriority(threadPriority);
}
return null;
}
});
univ.behaviorScheduler.initialize();
univ.behaviorScheduler.userStop = v.stopBehavior;
threadData = univ.behaviorScheduler.getThreadData(null, null);
threadData.thread = univ.behaviorScheduler;
threadData.threadType = J3dThread.BEHAVIOR_SCHEDULER;
threadData.lastUpdateTime = Math.max(currentTime,
threadData.lastUpdateTime);
}
createUpdateThread(v.renderBin);
createUpdateThread(v.soundScheduler);
if (v.physicalEnvironment != null) {
v.physicalEnvironment.addUser(v);
}
// create InputDeviceScheduler
evaluatePhysicalEnv(v);
regUniverseList.addUnique(univ);
views.addUnique(v);
}
/**
* This unregister a View with MasterControl.
* The View no longer has any Canvas3Ds in a container.
*/
private void unregisterView(View v) {
if (!views.remove(v)) {
v.active = false;
v.doneUnregister = true;
return; // already unregister
}
if (v.active) {
viewDeactivate(v);
}
if(J3dDebug.devPhase) {
J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
"MC: Destroy Sound Scheduler and RenderBin Update thread");
}
v.soundScheduler.updateThread.finish();
v.renderBin.updateThread.finish();
v.soundScheduler.updateThread = null;
v.renderBin.updateThread = null;
// remove VirtualUniverse related threads if Universe
// is empty
VirtualUniverse univ = v.universe;
synchronized (timeLock) {
// The reason we need to sync. with timeLock is because we
// don't want user thread running sendMessage() to
// dispatch it in different structure queue when
// part of the structure list is empty at the same time.
// This will cause inconsistence in the message reference
// count.
emptyMessageList(v.soundScheduler, v);
emptyMessageList(v.renderBin, v);
if (univ.isEmpty()) {
destroyUniverseThreads(univ);
} else {
emptyMessageList(univ.behaviorStructure, v);
emptyMessageList(univ.geometryStructure, v);
emptyMessageList(univ.soundStructure, v);
emptyMessageList(univ.renderingEnvironmentStructure, v);
emptyMessageList(univ.transformStructure, v);
}
}
if (v.physicalEnvironment != null) {
v.physicalEnvironment.removeUser(v);
}
// remove all InputDeviceScheduler if this is the last View
ArrayList<PhysicalEnvironment> list = new ArrayList<PhysicalEnvironment>();
for (Enumeration<PhysicalEnvironment> e = PhysicalEnvironment.physicalEnvMap.keys(); e.hasMoreElements();) {
PhysicalEnvironment phyEnv = e.nextElement();
InputDeviceScheduler sched = PhysicalEnvironment.physicalEnvMap.get(phyEnv);
boolean phyEnvHasUser = false;
for (int i = 0; i < phyEnv.users.size(); i++) {
if (views.contains(phyEnv.users.get(i))) {
// at least one registered view refer to it.
phyEnvHasUser = true;
break;
}
}
if (!phyEnvHasUser) {
if (J3dDebug.devPhase) {
J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
"MC: Destroy InputDeviceScheduler thread "
+ sched);
}
sched.finish();
phyEnv.inputsched = null;
list.add(phyEnv);
}
}
for (int i = 0; i < list.size(); i++) {
PhysicalEnvironment.physicalEnvMap.remove(list.get(i));
}
freeContext(v);
if (views.isEmpty()) {
if(J3dDebug.devPhase) {
J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
"MC: Destroy all Renderers");
}
// remove all Renderers if this is the last View
for (Enumeration<Renderer> e = Screen3D.deviceRendererMap.elements();
e.hasMoreElements(); ) {
Renderer rdr = e.nextElement();
Screen3D scr;
rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
runMonitor(RUN_RENDERER_CLEANUP, null, null, null, rdr);
scr = rdr.onScreen;
if (scr != null) {
if (scr.renderer != null) {
rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
runMonitor(RUN_RENDERER_CLEANUP, null, null,
null, scr.renderer);
scr.renderer = null;
}
}
scr = rdr.offScreen;
if (scr != null) {
if (scr.renderer != null) {
rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
runMonitor(RUN_RENDERER_CLEANUP, null, null,
null, scr.renderer);
scr.renderer = null;
}
}
rdr.onScreen = null;
rdr.offScreen = null;
}
// cleanup ThreadData corresponds to the view in renderer
for (Enumeration<Renderer> e = Screen3D.deviceRendererMap.elements();
e.hasMoreElements(); ) {
e.nextElement().cleanup();
}
// We have to reuse renderer even though MC exit
// see bug 4363279
// Screen3D.deviceRendererMap.clear();
} else {
// cleanup ThreadData corresponds to the view in renderer
for (Enumeration<Renderer> e = Screen3D.deviceRendererMap.elements();
e.hasMoreElements(); ) {
e.nextElement().cleanupView();
}
}
freeMessageList.add(univ);
freeMessageList.add(v);
evaluateAllCanvases();
stateWorkThreads.clear();
renderWorkThreads.clear();
requestRenderWorkThreads.clear();
threadListsChanged = true;
// This notify VirtualUniverse waitForMC() thread to continue
v.doneUnregister = true;
}
/**
* This procedure create MC thread that start together with MC.
*/
void createMCThreads() {
// There is only one renderingAttributesUpdate Thread globally
createUpdateThread(renderingAttributesStructure);
// Create timer thread
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
timerThread = new TimerThread(rootThreadGroup);
timerThread.setPriority(threadPriority);
}
return null;
}
});
timerThread.start();
// Create notification thread
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
notificationThread = new NotificationThread(rootThreadGroup);
notificationThread.setPriority(threadPriority);
}
return null;
}
});
notificationThread.start();
}
/**
* Destroy all VirtualUniverse related threads.
* This procedure may call two times when Locale detach in a
* live viewPlatform.
*/
private void destroyUniverseThreads(VirtualUniverse univ) {
if (regUniverseList.contains(univ)) {
if (J3dDebug.devPhase) {
J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
"MC: Destroy universe threads " + univ);
}
destroyUpdateThread(univ.behaviorStructure);
destroyUpdateThread(univ.geometryStructure);
destroyUpdateThread(univ.soundStructure);
destroyUpdateThread(univ.renderingEnvironmentStructure);
destroyUpdateThread(univ.transformStructure);
univ.behaviorScheduler.finish();
univ.behaviorScheduler.free();
univ.behaviorScheduler = null;
univ.initMCStructure();
activeUniverseList.remove(univ);
regUniverseList.remove(univ);
} else {
emptyMessageList(univ.behaviorStructure, null);
emptyMessageList(univ.geometryStructure, null);
emptyMessageList(univ.soundStructure, null);
emptyMessageList(univ.renderingEnvironmentStructure, null);
emptyMessageList(univ.transformStructure, null);
}
if (regUniverseList.isEmpty() && views.isEmpty()) {
if(J3dDebug.devPhase) {
J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
"MC: Destroy RenderingAttributes Update and Timer threads");
}
if (renderingAttributesStructure.updateThread != null) {
renderingAttributesStructure.updateThread.finish();
renderingAttributesStructure.updateThread = null;
}
renderingAttributesStructure.messageList.clear();
renderingAttributesStructure.objList = new ArrayList<J3dMessage>();
renderingAttributesStructure = new RenderingAttributesStructure();
if (timerThread != null) {
timerThread.finish();
timerThread = null;
}
if (notificationThread != null) {
notificationThread.finish();
notificationThread = null;
}
// shouldn't all of these be synchronized ???
synchronized (VirtualUniverse.mc.deviceScreenMap) {
deviceScreenMap.clear();
}
mirrorObjects.clear();
// Note: We should not clear the DISPLAYLIST/TEXTURE
// list here because other structure may release them
// later
for(int i=0; i<canvasIds.length; i++) {
canvasIds[i] = false;
}
canvasFreeIndex = 0;
renderOnceList.clear();
timestampUpdateList.clear();
defaultRenderMethod = null;
text3DRenderMethod = null;
vertexArrayRenderMethod = null;
displayListRenderMethod = null;
compressedGeometryRenderMethod = null;
orientedShape3DRenderMethod = null;
// Terminate MC thread
running = false;
}
}
/**
* Note that we have to go through all views instead of
* evaluate only the canvas in a single view since each screen
* may share by multiple view
*/
private void evaluateAllCanvases() {
synchronized (renderThreadData) {
// synchronized to prevent lost message when
// renderThreadData is clear
// First remove all renderrenderThreadData
renderThreadData.clear();
// Second reset canvasCount to zero
View viewArr[] = (View []) views.toArray(false);
for (int i=views.size()-1; i>=0; i--) {
viewArr[i].getCanvasList(true); // force canvas cache update
Screen3D screens[] = viewArr[i].getScreens();
for (int j=screens.length-1; j>=0; j--) {
screens[j].canvasCount = 0;
}
}
// Third create render thread and message thread
for (int i=views.size()-1; i>=0; i--) {
View v = viewArr[i];
Canvas3D canvasList[][] = v.getCanvasList(false);
if (!v.active) {
continue;
}
for (int j=canvasList.length-1; j>=0; j--) {
boolean added = false;
for (int k=canvasList[j].length-1; k>=0; k--) {
Canvas3D cv = canvasList[j][k];
final Screen3D screen = cv.screen;
if (cv.active) {
if (screen.canvasCount++ == 0) {
// Create Renderer, one per screen
if (screen.renderer == null) {
// get the renderer created for the graphics
// device of the screen of the canvas
// No need to synchronized since only
// MC use it.
Renderer rdr = Screen3D.deviceRendererMap.get(cv.screen.graphicsDevice);
if (rdr == null) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
screen.renderer
= new Renderer(
rootThreadGroup);
screen.renderer.setPriority(threadPriority);
}
return null;
}
});
screen.renderer.initialize();
Screen3D.deviceRendererMap.put(screen.graphicsDevice, screen.renderer);
} else {
screen.renderer = rdr;
}
}
}
// offScreen canvases will be handled by the
// request renderer, so don't add offScreen canvas
// the render list
//
// Issue 131: Automatic offscreen canvases need to
// be added to onscreen list. Special case.
//
// TODO KCR Issue 131: this should probably be
// changed to a list of screens since multiple
// off-screen canvases (either auto or manual) can
// be used by the same renderer
if (!cv.manualRendering) {
screen.renderer.onScreen = screen;
} else {
screen.renderer.offScreen = screen;
continue;
}
if (!added) {
// Swap message data thread, one per
// screen only. Note that we don't set
// lastUpdateTime for this thread so
// that it won't run in the first round
J3dThreadData renderData =
screen.renderer.getThreadData(v, null);
renderThreadData.add(renderData);
// only if renderBin is ready then we
// update the lastUpdateTime to make it run
if (v.renderBinReady) {
renderData.lastUpdateTime =
Math.max(currentTime,
renderData.lastUpdateTime);
}
added = true;
}
// Renderer message data thread
J3dThreadData renderData =
screen.renderer.getThreadData(v, cv);
renderThreadData.add(renderData);
if (v.renderBinReady) {
renderData.lastUpdateTime =
Math.max(currentTime,
renderData.lastUpdateTime);
}
}
}
}
}
}
threadListsChanged = true;
}
private void evaluatePhysicalEnv(View v) {
final PhysicalEnvironment env = v.physicalEnvironment;
if (env.inputsched == null) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
synchronized (rootThreadGroup) {
env.inputsched = new InputDeviceScheduler(
rootThreadGroup,
env);
env.inputsched.setPriority(threadPriority);
}
return null;
}
});
env.inputsched.start();
PhysicalEnvironment.physicalEnvMap.put(env, env.inputsched);
}
threadListsChanged = true;
}
final private void addToStateThreads(J3dThreadData threadData) {
if (threadData.thread.active) {
stateWorkThreads.add(threadData);
}
}
private void assignNewPrimaryView(VirtualUniverse univ) {
View currentPrimary = univ.getCurrentView();
if (currentPrimary != null) {
currentPrimary.primaryView = false;
}
View v[] = (View []) views.toArray(false);
int nviews = views.size();
for (int i=0; i<nviews; i++) {
View view = v[i];
if (view.active && view.isRunning &&
(univ == view.universe)) {
view.primaryView = true;
univ.setCurrentView(view);
return;
}
}
univ.setCurrentView(null);
}
/**
* This returns the default RenderMethod
*/
RenderMethod getDefaultRenderMethod() {
if (defaultRenderMethod == null) {
defaultRenderMethod = new DefaultRenderMethod();
}
return defaultRenderMethod;
}
/**
* This returns the text3d RenderMethod
*/
RenderMethod getText3DRenderMethod() {
if (text3DRenderMethod == null) {
text3DRenderMethod = new Text3DRenderMethod();
}
return text3DRenderMethod;
}
/**
* This returns the vertexArray RenderMethod
*/
RenderMethod getVertexArrayRenderMethod() {
if (vertexArrayRenderMethod == null) {
vertexArrayRenderMethod = new VertexArrayRenderMethod();
}
return vertexArrayRenderMethod;
}
/**
* This returns the displayList RenderMethod
*/
RenderMethod getDisplayListRenderMethod() {
if (displayListRenderMethod == null) {
displayListRenderMethod = new DisplayListRenderMethod();
}
return displayListRenderMethod;
}
/**
* This returns the compressed geometry RenderMethod
*/
RenderMethod getCompressedGeometryRenderMethod() {
if (compressedGeometryRenderMethod == null) {
compressedGeometryRenderMethod =
new CompressedGeometryRenderMethod();
}
return compressedGeometryRenderMethod;
}
/**
* This returns the oriented shape3d RenderMethod
*/
RenderMethod getOrientedShape3DRenderMethod() {
if (orientedShape3DRenderMethod == null) {
orientedShape3DRenderMethod = new OrientedShape3DRenderMethod();
}
return orientedShape3DRenderMethod;
}
/**
* This notifies MasterControl that the given view has been activated
*/
private void viewActivate(View v) {
VirtualUniverse univ = v.universe;
if (univ == null) {
return;
}
if (!views.contains(v) || !regUniverseList.contains(univ)) {
registerView(v);
} else if (v.active) {
evaluateAllCanvases();
return;
}
if ((univ.activeViewCount == 0)) {
univ.geometryStructure.resetConditionMet();
univ.behaviorStructure.resetConditionMet();
}
if (v.isRunning) {
numActiveViews++;
univ.activeViewCount++;
renderingAttributesStructure.updateThread.active = true;
univ.transformStructure.updateThread.active = true;
univ.geometryStructure.updateThread.active = true;
univ.soundStructure.updateThread.active = true;
univ.renderingEnvironmentStructure.updateThread.active = true;
}
univ.behaviorScheduler.active = true;
univ.behaviorStructure.updateThread.active = true;
activeUniverseList.addUnique(univ);
if (v.isRunning) {
v.soundScheduler.activate();
v.renderBin.updateThread.active = true;
}
v.active = true;
if (v.physicalEnvironment.activeViewRef++ == 0) {
v.physicalEnvironment.inputsched.activate();
}
if (univ.getCurrentView() == null) {
assignNewPrimaryView(univ);
}
evaluateAllCanvases();
v.inRenderThreadData = true;
threadListsChanged = true;
// Notify GeometryStructure to query visible atom again
// We should send message instead of just setting
// v.vDirtyMask = View.VISIBILITY_POLICY_DIRTY;
// since RenderBin may not run immediately next time.
// In this case the dirty flag will lost since
// updateViewCache() will reset it to 0.
v.renderBin.reactivateView = true;
}
/**
* Release context associate with view
*/
private void freeContext(View v) {
Canvas3D[][] canvasList = v.getCanvasList(false);
for (int j=canvasList.length-1; j>=0; j--) {
for (int k=canvasList[j].length-1; k>=0; k--) {
Canvas3D cv = canvasList[j][k];
if (!cv.validCanvas) {
if ((cv.screen != null) &&
(cv.screen.renderer != null)) {
rendererCleanupArgs[1] = cv;
rendererCleanupArgs[2] = FREECONTEXT_CLEANUP;
runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
cv.screen.renderer);
rendererCleanupArgs[1] = null;
}
}
}
}
}
/**
* This notifies MasterControl that the given view has been deactivated
*/
private void viewDeactivate(View v) {
if (!views.contains(v) || !v.active) {
v.active = false;
evaluateAllCanvases();
return;
}
VirtualUniverse univ = v.universe;
if (v.isRunning) {
// if stopView() invoke before, no need to decrement count
--numActiveViews;
--univ.activeViewCount;
}
if (numActiveViews == 0) {
renderingAttributesStructure.updateThread.active = false;
}
if (univ.activeViewCount == 0) {
// check if destroyUniverseThread invoked before
if (univ.behaviorScheduler != null) {
univ.behaviorScheduler.deactivate();
univ.transformStructure.updateThread.active = false;
univ.geometryStructure.updateThread.active = false;
univ.behaviorStructure.updateThread.active = false;
univ.soundStructure.updateThread.active = false;
univ.renderingEnvironmentStructure.updateThread.active
= false;
activeUniverseList.remove(univ);
}
}
v.soundScheduler.deactivate();
v.renderBin.updateThread.active = false;
v.active = false;
if (--v.physicalEnvironment.activeViewRef == 0) {
v.physicalEnvironment.inputsched.deactivate();
}
assignNewPrimaryView(univ);
evaluateAllCanvases();
freeContext(v);
v.inRenderThreadData = false;
threadListsChanged = true;
}
/**
* This notifies MasterControl to start given view
*/
private void startView(View v) {
if (!views.contains(v) || v.isRunning || !v.active) {
v.isRunning = true;
return;
}
numActiveViews++;
renderingAttributesStructure.updateThread.active = true;
VirtualUniverse univ = v.universe;
univ.activeViewCount++;
univ.transformStructure.updateThread.active = true;
univ.geometryStructure.updateThread.active = true;
univ.soundStructure.updateThread.active = true;
univ.renderingEnvironmentStructure.updateThread.active = true;
v.renderBin.updateThread.active = true;
v.soundScheduler.activate();
v.isRunning = true;
if (univ.getCurrentView() == null) {
assignNewPrimaryView(univ);
}
threadListsChanged = true;
}
/**
* This notifies MasterControl to stop given view
*/
private void stopView(View v) {
if (!views.contains(v) || !v.isRunning || !v.active) {
v.isRunning = false;
return;
}
if (--numActiveViews == 0) {
renderingAttributesStructure.updateThread.active = false;
}
VirtualUniverse univ = v.universe;
if (--univ.activeViewCount == 0) {
univ.transformStructure.updateThread.active = false;
univ.geometryStructure.updateThread.active = false;
univ.renderingEnvironmentStructure.updateThread.active = false;
univ.soundStructure.updateThread.active = false;
}
v.renderBin.updateThread.active = false;
v.soundScheduler.deactivate();
v.isRunning = false;
assignNewPrimaryView(univ);
threadListsChanged = true;
}
// Call from user thread
void addInputDeviceScheduler(InputDeviceScheduler ds) {
synchronized (inputDeviceThreads) {
inputDeviceThreads.add(ds);
if (inputDeviceThreads.size() == 1) {
timerThread.addInputDeviceSchedCond();
}
}
postRequest(INPUTDEVICE_CHANGE, null);
}
// Call from user thread
void removeInputDeviceScheduler(InputDeviceScheduler ds) {
inputDeviceThreads.remove(ds);
postRequest(INPUTDEVICE_CHANGE, null);
}
/**
* Add an object to the mirror object list
*/
void addMirrorObject(ObjectUpdate o) {
mirrorObjects.add(o);
}
/**
* This updates any mirror objects. It is called when threads
* are done.
*/
void updateMirrorObjects() {
ObjectUpdate objs[] = (ObjectUpdate []) mirrorObjects.toArray(false);
int sz = mirrorObjects.arraySize();
for (int i = 0; i< sz; i++) {
objs[i].updateObject();
}
mirrorObjects.clear();
}
/**
* This fun little method does all the hard work of setting up the
* work thread list.
*/
private void updateWorkThreads() {
stateWorkThreads.clear();
renderWorkThreads.clear();
requestRenderWorkThreads.clear();
// First the global rendering attributes structure update
if (numActiveViews > 0) {
addToStateThreads(renderingAttributesStructure.getUpdateThreadData());
}
// Next, each of the transform structure updates
VirtualUniverse universes[] = (VirtualUniverse [])
activeUniverseList.toArray(false);
VirtualUniverse univ;
int i;
int size = activeUniverseList.arraySize();
for (i=size-1; i>=0; i--) {
addToStateThreads(universes[i].transformStructure.getUpdateThreadData());
}
lastTransformStructureThread = stateWorkThreads.size();
// Next, the GeometryStructure, BehaviorStructure,
// RenderingEnvironmentStructure, and SoundStructure
for (i=size-1; i>=0; i--) {
univ = universes[i];
addToStateThreads(univ.geometryStructure.getUpdateThreadData());
addToStateThreads(univ.behaviorStructure.getUpdateThreadData());
addToStateThreads(univ.renderingEnvironmentStructure.getUpdateThreadData());
addToStateThreads(univ.soundStructure.getUpdateThreadData());
}
lastStructureUpdateThread = stateWorkThreads.size();
// Next, the BehaviorSchedulers
for (i=size-1; i>=0; i--) {
addToStateThreads(universes[i].behaviorScheduler.
getThreadData(null, null));
}
// Now InputDeviceScheduler
InputDeviceScheduler ds[] = (InputDeviceScheduler [])
inputDeviceThreads.toArray(true);
for (i=inputDeviceThreads.size()-1; i >=0; i--) {
J3dThreadData threadData = ds[i].getThreadData();
threadData.thread.active = true;
addToStateThreads(threadData);
}
// Now the RenderBins and SoundSchedulers
View viewArr[] = (View []) views.toArray(false);
J3dThreadData thread;
for (i=views.size()-1; i>=0; i--) {
View v = viewArr[i];
if (v.active && v.isRunning) {
addToStateThreads(v.renderBin.getUpdateThreadData());
addToStateThreads(v.soundScheduler.getUpdateThreadData());
Canvas3D canvasList[][] = v.getCanvasList(false);
int longestScreenList = v.getLongestScreenList();
Object args[] = null;
// renderer render
for (int j=0; j<longestScreenList; j++) {
for (int k=0; k < canvasList.length; k++) {
if (j < canvasList[k].length) {
Canvas3D cv = canvasList[k][j];
// Issue 131: setup renderer unless manualRendering
if (cv.active && cv.isRunningStatus && !cv.manualRendering ) {
if (cv.screen.renderer == null) {
continue;
}
thread = cv.screen.renderer.getThreadData(v, cv);
renderWorkThreads.add(thread);
args = (Object []) thread.threadArgs;
args[0] = RENDER;
args[1] = cv;
args[2] = v;
}
}
}
}
// renderer swap
for (int j=0; j<canvasList.length; j++) {
for (int k=0; k < canvasList[j].length; k++) {
Canvas3D cv = canvasList[j][k];
// create swap thread only if there is at
// least one active canvas
// Issue 131: only if not manualRendering
if (cv.active && cv.isRunningStatus && !cv.manualRendering) {
if (cv.screen.renderer == null) {
// Should not happen
continue;
}
thread = cv.screen.renderer.getThreadData(v, null);
renderWorkThreads.add(thread);
args = (Object []) thread.threadArgs;
args[0] = SWAP;
args[1] = v;
args[2] = canvasList[j];
break;
}
}
}
}
}
thread = null;
for (Enumeration<Renderer> e = Screen3D.deviceRendererMap.elements();
e.hasMoreElements(); ) {
Renderer rdr = e.nextElement();
thread = rdr.getThreadData(null, null);
requestRenderWorkThreads.add(thread);
thread.threadOpts = J3dThreadData.CONT_THREAD;
((Object[]) thread.threadArgs)[0] = REQUESTRENDER;
}
if (thread != null) {
thread.threadOpts |= J3dThreadData.WAIT_ALL_THREADS;
}
threadListsChanged = false;
// dumpWorkThreads();
}
void dumpWorkThreads() {
System.err.println("-----------------------------");
System.err.println("MasterControl/dumpWorkThreads");
J3dThreadData threads[];
int size = 0;
for (int k=0; k<3; k++) {
switch (k) {
case 0:
threads = (J3dThreadData []) stateWorkThreads.toArray(false);
size = stateWorkThreads.arraySize();
break;
case 1:
threads = (J3dThreadData []) renderWorkThreads.toArray(false);
size = renderWorkThreads.arraySize();
break;
default:
threads = (J3dThreadData []) requestRenderWorkThreads.toArray(false);
size = requestRenderWorkThreads.arraySize();
break;
}
for (int i=0; i<size; i++) {
J3dThreadData thread = threads[i];
System.err.println("Thread " + i + ": " + thread.thread);
System.err.println("\tOps: " + thread.threadOpts);
if (thread.threadArgs != null) {
Object[] args = (Object[]) thread.threadArgs;
System.err.print("\tArgs: ");
for (int j=0; j<args.length; j++) {
System.err.print(args[j] + " ");
}
}
System.err.println("");
}
}
System.err.println("-----------------------------");
}
/**
* A convienence wrapper function for various parts of the system
* to force MC to run.
*/
final void setWork() {
runMonitor(SET_WORK, null, null, null, null);
}
final void setWorkForRequestRenderer() {
runMonitor(SET_WORK_FOR_REQUEST_RENDERER, null, null, null, null);
}
/**
* Call from GraphicsConfigTemplate to evaluate current
* capabilities using Renderer thread to invoke native
* graphics library functions. This avoid MT-safe problem
* when using thread directly invoke graphics functions.
*/
void sendRenderMessage(GraphicsConfiguration gc,
Object arg, Integer mtype) {
Renderer rdr = createRenderer(gc);
J3dMessage renderMessage = new J3dMessage();
renderMessage.threads = J3dThread.RENDER_THREAD;
renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
renderMessage.universe = null;
renderMessage.view = null;
renderMessage.args[0] = null;
renderMessage.args[1] = arg;
renderMessage.args[2] = mtype;
rdr.rendererStructure.addMessage(renderMessage);
setWorkForRequestRenderer();
}
// Issue for Issue 175
// Pass DestroyCtxAndOffScreenBuffer to the Renderer thread for execution.
void sendDestroyCtxAndOffScreenBuffer(Canvas3D c) {
// Assertion check. Look for comment in sendCreateOffScreenBuffer.
GraphicsDevice gd = c.graphicsConfiguration.getDevice();
assert Screen3D.deviceRendererMap.get(gd) != null;
synchronized (mcThreadLock) {
// Issue 364: create master control thread if needed
createMasterControlThread();
assert mcThread != null;
Renderer rdr = createRenderer(c.graphicsConfiguration);
J3dMessage createMessage = new J3dMessage();
createMessage.threads = J3dThread.RENDER_THREAD;
createMessage.type = J3dMessage.DESTROY_CTX_AND_OFFSCREENBUFFER;
createMessage.universe = null;
createMessage.view = null;
createMessage.args[0] = c;
// Fix for issue 340: send display, drawable & ctx in msg
createMessage.args[1] = Long.valueOf(0L);
createMessage.args[2] = c.drawable;
createMessage.args[3] = c.ctx;
rdr.rendererStructure.addMessage(createMessage);
synchronized (requestObjList) {
setWorkForRequestRenderer();
pendingRequest = true;
}
}
}
// Fix for Issue 18
// Pass CreateOffScreenBuffer to the Renderer thread for execution.
void sendCreateOffScreenBuffer(Canvas3D c) {
// Assertion check that the renderer has already been created.
// If it hasn't, this is very, very bad because it opens up
// the possibility of an MT race condition since this method
// can be called from the user's thread, possibly at the same
// time as the MasterControl thread is trying to create a new
// Renderer. Fortunately, this should never happen since both
// the GraphicsTemplate3D methods that return a valid Graphics
// Configuration and the Canvas3D constructor will ultimately
// cause a renderer to be created via sendRenderMessage().
GraphicsDevice gd = c.graphicsConfiguration.getDevice();
J3dDebug.doAssert((Screen3D.deviceRendererMap.get(gd) != null),
"Screen3D.deviceRendererMap.get(gd) != null");
synchronized (mcThreadLock) {
// Create master control thread if needed
createMasterControlThread();
assert mcThread != null;
// Fix for Issue 72 : call createRenderer rather than getting
// the renderer from the canvas.screen object
Renderer rdr = createRenderer(c.graphicsConfiguration);
J3dMessage createMessage = new J3dMessage();
createMessage.threads = J3dThread.RENDER_THREAD;
createMessage.type = J3dMessage.CREATE_OFFSCREENBUFFER;
createMessage.universe = null;
createMessage.view = null;
createMessage.args[0] = c;
rdr.rendererStructure.addMessage(createMessage);
synchronized (requestObjList) {
setWorkForRequestRenderer();
pendingRequest = true;
}
}
}
// Issue 347 - Pass AllocateCanvasId to the Renderer thread for execution
void sendAllocateCanvasId(Canvas3D c) {
synchronized (mcThreadLock) {
// Issue 364: create master control thread if needed
createMasterControlThread();
assert mcThread != null;
Renderer rdr = createRenderer(c.graphicsConfiguration);
J3dMessage createMessage = new J3dMessage();
createMessage.threads = J3dThread.RENDER_THREAD;
createMessage.type = J3dMessage.ALLOCATE_CANVASID;
createMessage.universe = null;
createMessage.view = null;
createMessage.args[0] = c;
rdr.rendererStructure.addMessage(createMessage);
synchronized (requestObjList) {
setWorkForRequestRenderer();
pendingRequest = true;
}
}
}
// Issue 347 - Pass AllocateCanvasId to the Renderer thread for execution
void sendFreeCanvasId(Canvas3D c) {
synchronized (mcThreadLock) {
// Issue 364: create master control thread if needed
createMasterControlThread();
assert mcThread != null;
Renderer rdr = createRenderer(c.graphicsConfiguration);
J3dMessage createMessage = new J3dMessage();
createMessage.threads = J3dThread.RENDER_THREAD;
createMessage.type = J3dMessage.FREE_CANVASID;
createMessage.universe = null;
createMessage.view = null;
createMessage.args[0] = c;
rdr.rendererStructure.addMessage(createMessage);
synchronized (requestObjList) {
setWorkForRequestRenderer();
pendingRequest = true;
}
}
}
/**
* This is the MasterControl work method for Java 3D
*/
void doWork() {
runMonitor(CHECK_FOR_WORK, null, null, null, null);
synchronized (timeLock) {
synchronized (requestObjList) {
if (pendingRequest) {
handlePendingRequest();
}
}
}
if (!running) {
return;
}
if (threadListsChanged) { // Check for new Threads
updateWorkThreads();
}
synchronized (timeLock) {
// This is neccesary to prevent updating
// thread.lastUpdateTime from user thread
// in sendMessage() or sendRunMessage()
updateTimeValues();
}
//This is temporary until the view model is updated
View v[] = (View []) views.toArray(false);
for (int i=views.size()-1; i>=0; i--) {
if (v[i].active) {
v[i].updateViewCache();
// update OrientedShape3D
if ((v[i].viewCache.vcDirtyMask != 0 &&
!v[i].renderBin.orientedRAs.isEmpty()) ||
(v[i].renderBin.cachedDirtyOrientedRAs != null &&
!v[i].renderBin.cachedDirtyOrientedRAs.isEmpty())) {
v[i].renderBin.updateOrientedRAs();
}
}
}
runMonitor(RUN_THREADS, stateWorkThreads, renderWorkThreads,
requestRenderWorkThreads, null);
if (renderOnceList.size() > 0) {
clearRenderOnceList();
}
manageMemory();
}
private void handlePendingRequest() {
Object objs[];
Integer types[];
int size;
boolean rendererRun = false;
objs = requestObjList.toArray(false);
types = (Integer []) requestTypeList.toArray(false);
size = requestObjList.size();
for (int i=0; i < size; i++) {
// need to process request in order
Integer type = types[i];
Object o = objs[i];
if (type == RESET_CANVAS) {
Canvas3D cv = (Canvas3D) o;
if ((cv.screen != null) &&
(cv.screen.renderer != null)) {
rendererCleanupArgs[1] = o;
rendererCleanupArgs[2] = RESETCANVAS_CLEANUP;
runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
cv.screen.renderer);
rendererCleanupArgs[1] = null;
}
cv.reset();
cv.view = null;
cv.computeViewCache();
}
else if (type == ACTIVATE_VIEW) {
viewActivate((View) o);
}
else if (type == DEACTIVATE_VIEW) {
viewDeactivate((View) o);
} else if (type == REEVALUATE_CANVAS) {
evaluateAllCanvases();
} else if (type == INPUTDEVICE_CHANGE) {
inputDeviceThreads.clearMirror();
threadListsChanged = true;
} else if (type == START_VIEW) {
startView((View) o);
} else if (type == STOP_VIEW) {
View v = (View) o;
// Collision takes 3 rounds to finish its request
if (++v.stopViewCount > 4) {
v.stopViewCount = -1; // reset counter
stopView(v);
} else {
tempViewList.add(v);
}
} else if (type == UNREGISTER_VIEW) {
unregisterView((View) o);
} else if (type == PHYSICAL_ENV_CHANGE) {
evaluatePhysicalEnv((View) o);
} else if (type == EMPTY_UNIVERSE) {
// Issue 81: We need to process this message as long
// as there are no views associated with this
// universe. Previously, this message was ignored if
// there were views associated with *any* universe,
// which led to a memory / thread leak.
boolean foundView = false;
VirtualUniverse univ = (VirtualUniverse) o;
View v[] = (View []) views.toArray(false);
for (int j = views.size() - 1; j >= 0; j--) {
if (v[j].universe == univ) {
foundView = true;
break;
}
}
if (!foundView) {
destroyUniverseThreads(univ);
threadListsChanged = true;
}
} else if (type == START_RENDERER) {
if (o instanceof Canvas3D) {
Canvas3D c3d = (Canvas3D) o;
if (!c3d.isFatalError()) {
c3d.isRunningStatus = true;
}
} else {
((Renderer) o).userStop = false;
}
threadListsChanged = true;
} else if (type == STOP_RENDERER) {
if (o instanceof Canvas3D) {
((Canvas3D) o).isRunningStatus = false;
} else {
((Renderer) o).userStop = true;
}
threadListsChanged = true;
} else if (type == RENDER_ONCE) {
View v = (View) o;
// temporary start View for renderonce
// it will stop afterwards
startView(v);
renderOnceList.add(v);
sendRunMessage(v, J3dThread.UPDATE_RENDER);
threadListsChanged = true;
rendererRun = true;
} else if (type == FREE_CONTEXT) {
Canvas3D cv = (Canvas3D ) ((Object []) o)[0];
if ((cv.screen != null) &&
(cv.screen.renderer != null)) {
rendererCleanupArgs[1] = o;
rendererCleanupArgs[2] = REMOVECTX_CLEANUP;
runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
cv.screen.renderer);
rendererCleanupArgs[1] = null;
}
rendererRun = true;
} else if (type == FREE_DRAWING_SURFACE) {
Pipeline.getPipeline().freeDrawingSurfaceNative(o);
} else if (type == GETBESTCONFIG) {
GraphicsConfiguration gc = ((GraphicsConfiguration [])
((GraphicsConfigTemplate3D) o).testCfg)[0];
sendRenderMessage(gc, o, type);
rendererRun = true;
} else if (type == ISCONFIGSUPPORT) {
GraphicsConfiguration gc = (GraphicsConfiguration)
((GraphicsConfigTemplate3D) o).testCfg;
sendRenderMessage(gc, o, type);
rendererRun = true;
} else if ((type == SET_GRAPHICSCONFIG_FEATURES) ||
(type == SET_QUERYPROPERTIES)) {
GraphicsConfiguration gc = ((Canvas3D)o).graphicsConfiguration;
sendRenderMessage(gc, o, type);
rendererRun = true;
} else if (type == SET_VIEW) {
Canvas3D cv = (Canvas3D) o;
cv.view = cv.pendingView;
cv.computeViewCache();
}
}
// Do it only after all universe/View is register
for (int i=0; i < size; i++) {
Integer type = types[i];
if (type == FREE_MESSAGE) {
if (objs[i] instanceof VirtualUniverse) {
VirtualUniverse u = (VirtualUniverse) objs[i];
if (!regUniverseList.contains(u)) {
emptyMessageList(u.behaviorStructure, null);
emptyMessageList(u.geometryStructure, null);
emptyMessageList(u.soundStructure, null);
emptyMessageList(u.renderingEnvironmentStructure, null);
}
} else if (objs[i] instanceof View) {
View v = (View) objs[i];
if (!views.contains(v)) {
emptyMessageList(v.soundScheduler, v);
emptyMessageList(v.renderBin, v);
if (v.resetUnivCount == v.universeCount) {
v.reset();
v.universe = null;
if (running == false) {
// MC is about to terminate
/*
// Don't free list cause there may
// have some other thread returning ID
// after it.
FreeListManager.clearList(FreeListManager.DISPLAYLIST);
FreeListManager.clearList(FreeListManager.TEXTURE2D);
FreeListManager.clearList(FreeListManager.TEXTURE3D);
synchronized (textureIdLock) {
textureIdCount = 0;
}
*/
}
}
}
}
}
}
requestObjList.clear();
requestTypeList.clear();
size = tempViewList.size();
if (size > 0) {
if (running) {
for (int i=0; i < size; i++) {
requestTypeList.add(STOP_VIEW);
requestObjList.add(tempViewList.get(i));
}
setWork();
} else { // MC will shutdown
for (int i=0; i < size; i++) {
View v = (View) tempViewList.get(i);
v.stopViewCount = -1;
v.isRunning = false;
}
}
tempViewList.clear();
pendingRequest = true;
} else {
pendingRequest = rendererRun || (requestObjList.size() > 0);
}
size = freeMessageList.size();
if (size > 0) {
for (int i=0; i < size; i++) {
requestTypeList.add(FREE_MESSAGE);
requestObjList.add(freeMessageList.get(i));
}
pendingRequest = true;
freeMessageList.clear();
}
if (!running && (renderOnceList.size() > 0)) {
clearRenderOnceList();
}
if (pendingRequest) {
setWork();
}
if (rendererRun || requestRenderWorkToDo) {
running = true;
}
}
private void clearRenderOnceList() {
for (int i=renderOnceList.size()-1; i>=0; i--) {
View v = (View) renderOnceList.get(i);
v.renderOnceFinish = true;
// stop after render once
stopView(v);
}
renderOnceList.clear();
threadListsChanged = true;
}
synchronized void runMonitor(int action,
UnorderList stateThreadList,
UnorderList renderThreadList,
UnorderList requestRenderThreadList,
J3dThread nthread) {
switch (action) {
case RUN_THREADS:
int currentStateThread = 0;
int currentRenderThread = 0;
int currentRequestRenderThread = 0;
View view;
boolean done;
J3dThreadData thread;
J3dThreadData renderThreads[] = (J3dThreadData [])
renderThreadList.toArray(false);
J3dThreadData stateThreads[] = (J3dThreadData [])
stateThreadList.toArray(false);
J3dThreadData requestRenderThreads[] = (J3dThreadData [])
requestRenderThreadList.toArray(false);
int renderThreadSize = renderThreadList.arraySize();
int stateThreadSize = stateThreadList.arraySize();
int requestRenderThreadSize = requestRenderThreadList.arraySize();
done = false;
//lock all the needed geometry and image component
View[] allView = (View []) views.toArray(false);
View currentV;
int i;
if (lockGeometry)
{
for( i = views.arraySize()-1; i >= 0; i--) {
currentV = allView[i];
currentV.renderBin.lockGeometry();
}
}
while (!done) {
// First try a RenderThread
while (!renderWaiting &&
currentRenderThread != renderThreadSize) {
thread = renderThreads[currentRenderThread++];
if (!thread.needsRun) {
continue;
}
if ((thread.threadOpts & J3dThreadData.START_TIMER) != 0) {
view = (View)((Object[])thread.threadArgs)[2];
view.frameNumber++;
view.startTime = J3dClock.currentTimeMillis();
}
renderPending++;
if (cpuLimit == 1) {
thread.thread.args = (Object[])thread.threadArgs;
thread.thread.doWork(currentTime);
} else {
threadPending++;
thread.thread.runMonitor(J3dThread.RUN,
currentTime,
(Object[])thread.threadArgs);
}
if ((thread.threadOpts & J3dThreadData.STOP_TIMER) != 0) {
view = (View)((Object[])thread.threadArgs)[3];
timestampUpdateList.add(view);
}
if ((thread.threadOpts & J3dThreadData.LAST_STOP_TIMER) != 0) {
// release lock on locked geometry and image component
for( i = 0; i < views.arraySize(); i++) {
currentV = allView[i];
currentV.renderBin.releaseGeometry();
}
}
if ((cpuLimit != 1) &&
(thread.threadOpts &
J3dThreadData.WAIT_ALL_THREADS) != 0) {
renderWaiting = true;
}
if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
state = WAITING_FOR_CPU;
try {
wait();
} catch (InterruptedException e) {
System.err.println(e);
}
state = RUNNING;
}
}
// Now try state threads
while (!stateWaiting &&
currentStateThread != stateThreadSize) {
thread = stateThreads[currentStateThread++];
if (!thread.needsRun) {
continue;
}
statePending++;
if (cpuLimit == 1) {
thread.thread.args = (Object[])thread.threadArgs;
thread.thread.doWork(currentTime);
} else {
threadPending++;
thread.thread.runMonitor(J3dThread.RUN,
currentTime,
(Object[])thread.threadArgs);
}
if (cpuLimit != 1 && (thread.threadOpts &
J3dThreadData.WAIT_ALL_THREADS) != 0) {
stateWaiting = true;
}
if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
// Fix bug 4686766 - always allow
// renderer thread to continue if not finish
// geomLock can release for Behavior thread to
// continue.
if (currentRenderThread == renderThreadSize) {
state = WAITING_FOR_CPU;
try {
wait();
} catch (InterruptedException e) {
System.err.println(e);
}
state = RUNNING;
} else {
// Run renderer thread next time
break;
}
}
}
// Now try requestRender threads
if (!renderWaiting &&
(currentRenderThread == renderThreadSize)) {
currentRequestRenderThread = 0;
while (!renderWaiting &&
(currentRequestRenderThread !=
requestRenderThreadSize)) {
thread =
requestRenderThreads[currentRequestRenderThread++];
renderPending++;
if (cpuLimit == 1) {
thread.thread.args = (Object[])thread.threadArgs;
thread.thread.doWork(currentTime);
} else {
threadPending++;
thread.thread.runMonitor(J3dThread.RUN,
currentTime,
(Object[])thread.threadArgs);
}
if (cpuLimit != 1 && (thread.threadOpts &
J3dThreadData.WAIT_ALL_THREADS) != 0) {
renderWaiting = true;
}
if (cpuLimit != 1 && cpuLimit <= threadPending) {
state = WAITING_FOR_CPU;
try {
wait();
} catch (InterruptedException e) {
System.err.println(e);
}
state = RUNNING;
}
}
}
if (cpuLimit != 1) {
if ((renderWaiting &&
(currentStateThread == stateThreadSize)) ||
(stateWaiting &&
currentRenderThread == renderThreadSize) ||
(renderWaiting && stateWaiting)) {
if (!requestRenderWorkToDo) {
state = WAITING_FOR_THREADS;
try {
wait();
} catch (InterruptedException e) {
System.err.println(e);
}
state = RUNNING;
}
requestRenderWorkToDo = false;
}
}
if ((currentStateThread == stateThreadSize) &&
(currentRenderThread == renderThreadSize) &&
(currentRequestRenderThread == requestRenderThreadSize) &&
(threadPending == 0)) {
for (int k = timestampUpdateList.size() - 1; k >= 0; k--) {
View v = timestampUpdateList.get(k);
v.setFrameTimingValues();
v.universe.behaviorStructure.incElapsedFrames();
}
timestampUpdateList.clear();
updateMirrorObjects();
done = true;
if (isStatsLoggable(Level.INFO)) {
// Instrumentation of Java 3D renderer
logTimes();
}
}
}
break;
case THREAD_DONE:
if (state != WAITING_FOR_RENDERER_CLEANUP) {
threadPending--;
assert threadPending >= 0 : ("threadPending = " + threadPending);
if (nthread.type == J3dThread.RENDER_THREAD) {
View v = (View) nthread.args[3];
if (v != null) { // STOP_TIMER
v.stopTime = J3dClock.currentTimeMillis();
}
if (--renderPending == 0) {
renderWaiting = false;
}
assert renderPending >= 0 : ("renderPending = " + renderPending);
} else {
if (--statePending == 0) {
stateWaiting = false;
}
assert statePending >= 0 : ("statePending = " + statePending);
}
if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS) {
notify();
}
} else {
notify();
state = RUNNING;
}
break;
case CHECK_FOR_WORK:
if (!workToDo) {
state = SLEEPING;
// NOTE: this could wakeup spuriously (see issue 279), but it
// will not cause any problems.
try {
wait();
} catch (InterruptedException e) {
System.err.println(e);
}
state = RUNNING;
}
workToDo = false;
break;
case SET_WORK:
workToDo = true;
if (state == SLEEPING) {
notify();
}
break;
case SET_WORK_FOR_REQUEST_RENDERER:
requestRenderWorkToDo = true;
workToDo = true;
if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS ||
state == SLEEPING) {
notify();
}
break;
case RUN_RENDERER_CLEANUP:
nthread.runMonitor(J3dThread.RUN, currentTime,
rendererCleanupArgs);
state = WAITING_FOR_RENDERER_CLEANUP;
// Issue 279 - loop until state is set to running
while (state != RUNNING) {
try {
wait();
} catch (InterruptedException e) {
System.err.println(e);
}
}
break;
default:
// Should never get here
assert false : "missing case in switch statement";
}
}
// Static initializer
static {
// create ThreadGroup
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Object>() {
@Override
public Object run() {
ThreadGroup parent;
Thread thread = Thread.currentThread();
threadPriority = thread.getPriority();
rootThreadGroup = thread.getThreadGroup();
while ((parent = rootThreadGroup.getParent()) != null) {
rootThreadGroup = parent;
}
rootThreadGroup = new ThreadGroup(rootThreadGroup,
"Java3D");
// use the default maximum group priority
return null;
}
});
// Initialize loggers
try {
initLoggers();
} catch (RuntimeException ex) {
System.err.println(ex);
}
}
static String mtype[] = {
"INSERT_NODES",
"REMOVE_NODES",
"RUN",
"TRANSFORM_CHANGED",
"UPDATE_VIEW",
"STOP_THREAD",
"COLORINGATTRIBUTES_CHANGED",
"LINEATTRIBUTES_CHANGED",
"POINTATTRIBUTES_CHANGED",
"POLYGONATTRIBUTES_CHANGED",
"RENDERINGATTRIBUTES_CHANGED",
"TEXTUREATTRIBUTES_CHANGED",
"TRANSPARENCYATTRIBUTES_CHANGED",
"MATERIAL_CHANGED",
"TEXCOORDGENERATION_CHANGED",
"TEXTURE_CHANGED",
"MORPH_CHANGED",
"GEOMETRY_CHANGED",
"APPEARANCE_CHANGED",
"LIGHT_CHANGED",
"BACKGROUND_CHANGED",
"CLIP_CHANGED",
"FOG_CHANGED",
"BOUNDINGLEAF_CHANGED",
"SHAPE3D_CHANGED",
"TEXT3D_TRANSFORM_CHANGED",
"TEXT3D_DATA_CHANGED",
"SWITCH_CHANGED",
"COND_MET",
"BEHAVIOR_ENABLE",
"BEHAVIOR_DISABLE",
"INSERT_RENDERATOMS",
"ORDERED_GROUP_INSERTED",
"ORDERED_GROUP_REMOVED",
"COLLISION_BOUND_CHANGED",
"REGION_BOUND_CHANGED",
"MODELCLIP_CHANGED",
"BOUNDS_AUTO_COMPUTE_CHANGED",
"SOUND_ATTRIB_CHANGED",
"AURALATTRIBUTES_CHANGED",
"SOUNDSCAPE_CHANGED",
"ALTERNATEAPPEARANCE_CHANGED",
"RENDER_OFFSCREEN",
"RENDER_RETAINED",
"RENDER_IMMEDIATE",
"SOUND_STATE_CHANGED",
"ORIENTEDSHAPE3D_CHANGED",
"TEXTURE_UNIT_STATE_CHANGED",
"UPDATE_VIEWPLATFORM",
"BEHAVIOR_ACTIVATE",
"GEOMETRYARRAY_CHANGED",
"MEDIA_CONTAINER_CHANGED",
"RESIZE_CANVAS",
"TOGGLE_CANVAS",
"IMAGE_COMPONENT_CHANGED",
"SCHEDULING_INTERVAL_CHANGED",
"VIEWSPECIFICGROUP_CHANGED",
"VIEWSPECIFICGROUP_INIT",
"VIEWSPECIFICGROUP_CLEAR",
"ORDERED_GROUP_TABLE_CHANGED",
"BEHAVIOR_REEVALUATE",
"CREATE_OFFSCREENBUFFER",
"DESTROY_CTX_AND_OFFSCREENBUFFER",
"SHADER_ATTRIBUTE_CHANGED",
"SHADER_ATTRIBUTE_SET_CHANGED",
"SHADER_APPEARANCE_CHANGED",
"ALLOCATE_CANVASID",
"FREE_CANVASID",
};
private String dumpThreads(int threads) {
StringBuffer strBuf = new StringBuffer();
strBuf.append("threads:");
//dump Threads type
if ((threads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
strBuf.append(" BEHAVIOR_SCHEDULER");
}
if ((threads & J3dThread.SOUND_SCHEDULER) != 0) {
strBuf.append(" SOUND_SCHEDULER");
}
if ((threads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
strBuf.append(" INPUT_DEVICE_SCHEDULER");
}
if ((threads & J3dThread.RENDER_THREAD) != 0) {
strBuf.append(" RENDER_THREAD");
}
if ((threads & J3dThread.UPDATE_GEOMETRY) != 0) {
strBuf.append(" UPDATE_GEOMETRY");
}
if ((threads & J3dThread.UPDATE_RENDER) != 0) {
strBuf.append(" UPDATE_RENDER");
}
if ((threads & J3dThread.UPDATE_BEHAVIOR) != 0) {
strBuf.append(" UPDATE_BEHAVIOR");
}
if ((threads & J3dThread.UPDATE_SOUND) != 0) {
strBuf.append(" UPDATE_SOUND");
}
if ((threads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
strBuf.append(" UPDATE_RENDERING_ATTRIBUTES");
}
if ((threads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
strBuf.append(" UPDATE_RENDERING_ENVIRONMENT");
}
if ((threads & J3dThread.UPDATE_TRANSFORM) != 0) {
strBuf.append(" UPDATE_TRANSFORM");
}
return strBuf.toString();
}
// Method to log the specified message. Note that the caller
// should check for isCoreLoggable(FINEST) before calling
private void dumpMessage(String str, J3dMessage m) {
StringBuffer strBuf = new StringBuffer();
strBuf.append(str).append(" ");
if (m.type >= 0 && m.type < mtype.length) {
strBuf.append(mtype[m.type]);
} else {
strBuf.append("<UNKNOWN>");
}
strBuf.append(" ").append(dumpThreads(m.threads));
getCoreLogger().finest(strBuf.toString());
}
int frameCount = 0;
private int frameCountCutoff = 100;
private void manageMemory() {
if (++frameCount > frameCountCutoff) {
FreeListManager.manageLists();
frameCount = 0;
}
}
/**
* Yields the current thread, by sleeping for a small amount of
* time. Unlike <code>Thread.yield()</code>, this method
* guarantees that the current thread will yield to another thread
* waiting to run. It also ensures that the other threads will
* run for at least a small amount of time before the current
* thread runs again.
*/
static final void threadYield() {
// Note that we can't just use Thread.yield(), since it
// doesn't guarantee that it will actually yield the thread
// (and, in fact, it appears to be a no-op under Windows). So
// we will sleep for 1 msec instead. Since most threads use
// wait/notify, and only use this when they are waiting for
// another thread to finish something, this shouldn't be a
// performance concern.
//Thread.yield();
try {
Thread.sleep(1);
}
catch (InterruptedException e) {
// Do nothing, since we really don't care how long (or
// even whether) we sleep
}
}
// Return the number of available processors
private int getNumberOfProcessors() {
return Runtime.getRuntime().availableProcessors();
}
//
// The following framework supports code instrumentation. To use it,
// add code of the following form to areas that you want to enable for
// timing:
//
// long startTime = System.nanoTime();
// sortTransformGroups(tSize, tgs);
// long deltaTime = System.nanoTime() - startTime;
// VirtualUniverse.mc.recordTime(MasterControl.TimeType.XXXXX, deltaTime);
//
// where "XXXXX" is the enum representing the code segment being timed.
// Additional enums can be defined for new subsystems.
//
static enum TimeType {
TOTAL_FRAME,
RENDER,
BEHAVIOR,
// TRANSFORM_UPDATE,
// ...
}
private long[] statTimes = new long[TimeType.values().length];
private int[] statCounts = new int[TimeType.values().length];
private boolean[] statSeen = new boolean[TimeType.values().length];
private int frameCycleTick = 0;
private long frameCycleNumber = 0L;
// Method to record times -- should not be called unless the stats logger
// level is set to INFO or lower
synchronized void recordTime(TimeType type, long deltaTime) {
int idx = type.ordinal();
statTimes[idx] += deltaTime;
statCounts[idx]++;
statSeen[idx] = true;
}
// Method to record times -- this is not called unless the stats logger
// level is set to INFO or lower
private synchronized void logTimes() {
++frameCycleNumber;
if (++frameCycleTick >= 10) {
StringBuffer strBuf = new StringBuffer();
strBuf.append("----------------------------------------------\n").
append(" Frame Number = ").
append(frameCycleNumber).
append("\n");
for (int i = 0; i < statTimes.length; i++) {
if (statSeen[i]) {
strBuf.append(" ");
if (statCounts[i] > 0) {
strBuf.append(TimeType.values()[i]).
append(" [").
append(statCounts[i]).
append("] = ").
append((double)statTimes[i] / 1000000.0 / (double)statCounts[i]).
append(" msec per call\n");
statTimes[i] = 0L;
statCounts[i] = 0;
} else {
assert statTimes[i] == 0L;
strBuf.append(TimeType.values()[i]).
append(" [0] = 0.0 msec\n");
}
}
}
getStatsLogger().info(strBuf.toString());
frameCycleTick = 0;
}
}
}