/* * Copyright 1997-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.Point; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.logging.Level; class Renderer extends J3dThread { // This action causes this thread to wait static final int WAIT = 0; // This action causes this thread to notify the view, and then wait. static final int NOTIFY_AND_WAIT = 1; // This action causes this thread to be notified static final int NOTIFY = 2; // The following are DecalGroup rendering states static final int DECAL_NONE = 0; static final int DECAL_1ST_CHILD = 1; static final int DECAL_NTH_CHILD = 2; // stuff for scene antialiasing static final int NUM_ACCUMULATION_SAMPLES = 8; static final float ACCUM_SAMPLES_X[] = { -0.54818f, 0.56438f, 0.39462f, -0.54498f, -0.83790f, -0.39263f, 0.32254f, 0.84216f}; static final float ACCUM_SAMPLES_Y[] = { 0.55331f, -0.53495f, 0.41540f, -0.52829f, 0.82102f, -0.27383f, 0.09133f, -0.84399f}; static final float accumValue = 1.0f / NUM_ACCUMULATION_SAMPLES; // The following are Render arguments static final int RENDER = 0; static final int SWAP = 1; static final int REQUESTRENDER = 2; static final int REQUESTCLEANUP = 3; // Renderer Structure used for the messaging to the renderer RendererStructure rendererStructure = new RendererStructure(); // vworldtoVpc matrix for background geometry Transform3D bgVworldToVpc = new Transform3D(); private static int numInstances = 0; private int instanceNum = -1; // Local copy of sharedStereZBuffer flag boolean sharedStereoZBuffer; // This is the id for the underlying sharable graphics context Context sharedCtx = null; // since the sharedCtx id can be the same as the previous one, // we need to keep a time stamp to differentiate the contexts with the // same id long sharedCtxTimeStamp = 0; // display and drawable, used to free shared context private Drawable sharedCtxDrawable = null; /** * This is the id of the current rendering context */ Context currentCtx = null; /** * This is the id of the current rendering drawable */ Drawable currentDrawable = null; // an unique bit to identify this renderer int rendererBit = 0; // an unique number to identify this renderer : ( rendererBit = 1 << rendererId) int rendererId = 0; // List of renderMolecules that are dirty due to additions // or removal of renderAtoms from their display list set // of renderAtoms ArrayList dirtyRenderMoleculeList = new ArrayList(); // List of individual dlists that need to be rebuilt ArrayList dirtyRenderAtomList = new ArrayList(); // List of (Rm, rInfo) pair of individual dlists that need to be rebuilt ArrayList dirtyDlistPerRinfoList = new ArrayList(); // Texture and display list that should be freed ArrayList textureIdResourceFreeList = new ArrayList(); ArrayList displayListResourceFreeList = new ArrayList(); // Texture that should be reload ArrayList textureReloadList = new ArrayList(); J3dMessage[] renderMessage; // The screen for this Renderer. Note that this renderer may share // by both on screen and off screen. When view unregister, we need // to set both reference to null. Screen3D onScreen; Screen3D offScreen; // full screen anti-aliasing projection matrices Transform3D accumLeftProj = new Transform3D(); Transform3D accumRightProj = new Transform3D(); Transform3D accumInfLeftProj = new Transform3D(); Transform3D accumInfRightProj = new Transform3D(); // rendering messages J3dMessage m[]; int nmesg = 0; // List of contexts created ArrayList listOfCtxs = new ArrayList(); // Parallel list of canvases ArrayList listOfCanvases = new ArrayList(); boolean needToRebuildDisplayList = false; // True when either one of dirtyRenderMoleculeList, // dirtyDlistPerRinfoList, dirtyRenderAtomList size > 0 boolean dirtyDisplayList = false; // Remember OGL context resources to free // before context is destroy. // It is used when sharedCtx = true; ArrayList textureIDResourceTable = new ArrayList(5); // Instrumentation of Java 3D renderer private long lastSwapTime = System.nanoTime(); private synchronized int newInstanceNum() { return (++numInstances); } @Override int getInstanceNum() { if (instanceNum == -1) instanceNum = newInstanceNum(); return instanceNum; } /** * Constructs a new Renderer */ Renderer(ThreadGroup t) { super(t); setName("J3D-Renderer-" + getInstanceNum()); type = J3dThread.RENDER_THREAD; rendererId = VirtualUniverse.mc.getRendererId(); rendererBit = (1 << rendererId); renderMessage = new J3dMessage[1]; } /** * The main loop for the renderer. */ @Override void doWork(long referenceTime) { RenderBin renderBin = null; Canvas3D cv, canvas=null; Object firstArg; View view = null; int stereo_mode; int num_stereo_passes, num_accum_passes = 1; int pass, apass, i, j; boolean doAccum = false; double accumDx = 0.0f, accumDy = 0.0f; double accumDxFactor = 1.0f, accumDyFactor = 1.0f; double accumLeftX = 0.0, accumLeftY = 0.0, accumRightX = 0.0, accumRightY = 0.0, accumInfLeftX = 0.0, accumInfLeftY = 0.0, accumInfRightX = 0.0, accumInfRightY = 0.0; int opArg; Transform3D t3d = null; opArg = ((Integer)args[0]).intValue(); try { if (opArg == SWAP) { Object [] swapArray = (Object[])args[2]; view = (View)args[3]; for (i=0; i 0; c.userStencilAvailable = (userOwnsStencil && (c.actualStencilSize > 0)); c.systemStencilAvailable = (!userOwnsStencil && (c.actualStencilSize > 0)); c.sceneAntialiasingMultiSamplesAvailable = c.hasSceneAntialiasingMultisample(); if (c.sceneAntialiasingMultiSamplesAvailable) { c.sceneAntialiasingAvailable = true; } else { c.sceneAntialiasingAvailable = c.hasSceneAntialiasingAccum(); } } catch (RuntimeException ex) { ex.printStackTrace(); // Issue 260 : indicate fatal error and notify error listeners c.setFatalError(); RenderingError err = new RenderingError(RenderingError.GRAPHICS_CONFIG_ERROR, J3dI18N.getString("Renderer1")); err.setCanvas3D(c); err.setGraphicsDevice(c.graphicsConfiguration.getDevice()); notifyErrorListeners(err); } GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY); } else if (reqType == MasterControl.SET_QUERYPROPERTIES){ try { c.createQueryContext(); } catch (RuntimeException ex) { ex.printStackTrace(); // Issue 260 : indicate fatal error and notify error listeners c.setFatalError(); RenderingError err = new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, J3dI18N.getString("Renderer2")); err.setCanvas3D(c); err.setGraphicsDevice(c.graphicsConfiguration.getDevice()); notifyErrorListeners(err); } // currentCtx change after we create a new context GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY); currentCtx = null; currentDrawable = null; } } else if (secondArg instanceof Integer) { // Issue 121 - This was formerly used as a message from // the now-nonexistant TextureRetained finalize() method // to free the texture id throw new AssertionError(); } else if (secondArg instanceof GeometryArrayRetained) { // message from GeometryArrayRetained // clearLive() to free D3D array //((GeometryArrayRetained) secondArg).freeD3DArray(false); } else if (secondArg instanceof GraphicsConfigTemplate3D) { GraphicsConfigTemplate3D gct = (GraphicsConfigTemplate3D) secondArg; Integer reqType = (Integer) m[nmesg].args[2]; if (reqType == MasterControl.GETBESTCONFIG) { GraphicsConfiguration gcfg = null; GraphicsConfiguration [] gcList = (GraphicsConfiguration []) gct.testCfg; try { gcfg = Pipeline.getPipeline().getBestConfiguration(gct, gcList); } catch (NullPointerException npe) { npe.printStackTrace(); } catch (RuntimeException ex) { ex.printStackTrace(); // Issue 260 : notify error listeners RenderingError err = new RenderingError(RenderingError.GRAPHICS_CONFIG_ERROR, J3dI18N.getString("Renderer3")); err.setGraphicsDevice(gcList[0].getDevice()); notifyErrorListeners(err); } gct.testCfg = gcfg; } else if (reqType == MasterControl.ISCONFIGSUPPORT) { boolean rval = false; GraphicsConfiguration gc = (GraphicsConfiguration) gct.testCfg; try { if (Pipeline.getPipeline().isGraphicsConfigSupported(gct, gc)) { rval = true; } } catch (NullPointerException npe) { npe.printStackTrace(); } catch (RuntimeException ex) { ex.printStackTrace(); // Issue 260 : notify error listeners RenderingError err = new RenderingError(RenderingError.GRAPHICS_CONFIG_ERROR, J3dI18N.getString("Renderer4")); err.setGraphicsDevice(gc.getDevice()); notifyErrorListeners(err); } gct.testCfg = Boolean.valueOf(rval); } GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY); } m[nmesg++].decRefcount(); continue; } canvas = (Canvas3D) firstArg; renderType = m[nmesg].type; if (renderType == J3dMessage.CREATE_OFFSCREENBUFFER) { // Fix for issue 18. // Fix for issue 20. canvas.drawable = null; try { // Issue 396. Pass in a null ctx for 2 reasons : // 1) We should not use ctx field directly without buffering in a msg. // 2) canvas.ctx should be null. canvas.drawable = canvas.createOffScreenBuffer(null, canvas.offScreenCanvasSize.width, canvas.offScreenCanvasSize.height); } catch (RuntimeException ex) { ex.printStackTrace(); } if (canvas.drawable == null) { // Issue 260 : indicate fatal error and notify error listeners canvas.setFatalError(); RenderingError err = new RenderingError(RenderingError.OFF_SCREEN_BUFFER_ERROR, J3dI18N.getString("Renderer5")); err.setCanvas3D(canvas); err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); notifyErrorListeners(err); } canvas.offScreenBufferPending = false; m[nmesg++].decRefcount(); continue; } else if (renderType == J3dMessage.DESTROY_CTX_AND_OFFSCREENBUFFER) { Object[] obj = m[nmesg].args; // Fix for issue 175: destroy ctx & off-screen buffer // Fix for issue 340: get display, drawable & ctx from msg removeCtx(canvas, (Drawable) obj[2], (Context) obj[3], false, !canvas.offScreen, true); canvas.offScreenBufferPending = false; m[nmesg++].decRefcount(); continue; } else if (renderType == J3dMessage.ALLOCATE_CANVASID) { canvas.allocateCanvasId(); } else if (renderType == J3dMessage.FREE_CANVASID) { canvas.freeCanvasId(); } if ((canvas.view == null) || !canvas.firstPaintCalled) { // This happen when the canvas just remove from the View if (renderType == J3dMessage.RENDER_OFFSCREEN) { canvas.offScreenRendering = false; } m[nmesg++].decRefcount(); continue; } if (!canvas.validCanvas && (renderType != J3dMessage.RENDER_OFFSCREEN)) { m[nmesg++].decRefcount(); continue; } if (renderType == J3dMessage.RESIZE_CANVAS) { // render the image again after resize VirtualUniverse.mc.sendRunMessage(canvas.view, J3dThread.RENDER_THREAD); m[nmesg++].decRefcount(); } else if (renderType == J3dMessage.TOGGLE_CANVAS) { VirtualUniverse.mc.sendRunMessage(canvas.view, J3dThread.RENDER_THREAD); m[nmesg++].decRefcount(); } else if (renderType == J3dMessage.RENDER_IMMEDIATE) { int command = ((Integer)m[nmesg].args[1]).intValue(); //System.err.println("command= " + command); if (canvas.isFatalError()) { continue; } try { switch (command) { case GraphicsContext3D.CLEAR: canvas.graphicsContext3D.doClear(); break; case GraphicsContext3D.DRAW: canvas.graphicsContext3D.doDraw( (Geometry)m[nmesg].args[2]); break; case GraphicsContext3D.SWAP: canvas.doSwap(); break; case GraphicsContext3D.READ_RASTER: canvas.graphicsContext3D.doReadRaster( (Raster)m[nmesg].args[2]); break; case GraphicsContext3D.SET_APPEARANCE: canvas.graphicsContext3D.doSetAppearance( (Appearance)m[nmesg].args[2]); break; case GraphicsContext3D.SET_BACKGROUND: canvas.graphicsContext3D.doSetBackground( (Background)m[nmesg].args[2]); break; case GraphicsContext3D.SET_FOG: canvas.graphicsContext3D.doSetFog( (Fog)m[nmesg].args[2]); break; case GraphicsContext3D.SET_LIGHT: canvas.graphicsContext3D.doSetLight( (Light)m[nmesg].args[2], ((Integer)m[nmesg].args[3]).intValue()); break; case GraphicsContext3D.INSERT_LIGHT: canvas.graphicsContext3D.doInsertLight( (Light)m[nmesg].args[2], ((Integer)m[nmesg].args[3]).intValue()); break; case GraphicsContext3D.REMOVE_LIGHT: canvas.graphicsContext3D.doRemoveLight( ((Integer)m[nmesg].args[2]).intValue()); break; case GraphicsContext3D.ADD_LIGHT: canvas.graphicsContext3D.doAddLight( (Light)m[nmesg].args[2]); break; case GraphicsContext3D.SET_HI_RES: canvas.graphicsContext3D.doSetHiRes( (HiResCoord)m[nmesg].args[2]); break; case GraphicsContext3D.SET_MODEL_TRANSFORM: t3d = (Transform3D)m[nmesg].args[2]; canvas.graphicsContext3D.doSetModelTransform(t3d); break; case GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM: t3d = (Transform3D)m[nmesg].args[2]; canvas.graphicsContext3D.doMultiplyModelTransform(t3d); break; case GraphicsContext3D.SET_SOUND: canvas.graphicsContext3D.doSetSound( (Sound)m[nmesg].args[2], ((Integer)m[nmesg].args[3]).intValue()); break; case GraphicsContext3D.INSERT_SOUND: canvas.graphicsContext3D.doInsertSound( (Sound)m[nmesg].args[2], ((Integer)m[nmesg].args[3]).intValue()); break; case GraphicsContext3D.REMOVE_SOUND: canvas.graphicsContext3D.doRemoveSound( ((Integer)m[nmesg].args[2]).intValue()); break; case GraphicsContext3D.ADD_SOUND: canvas.graphicsContext3D.doAddSound( (Sound)m[nmesg].args[2]); break; case GraphicsContext3D.SET_AURAL_ATTRIBUTES: canvas.graphicsContext3D.doSetAuralAttributes( (AuralAttributes)m[nmesg].args[2]); break; case GraphicsContext3D.SET_BUFFER_OVERRIDE: canvas.graphicsContext3D.doSetBufferOverride( ((Boolean)m[nmesg].args[2]).booleanValue()); break; case GraphicsContext3D.SET_FRONT_BUFFER_RENDERING: canvas.graphicsContext3D.doSetFrontBufferRendering( ((Boolean)m[nmesg].args[2]).booleanValue()); break; case GraphicsContext3D.SET_STEREO_MODE: canvas.graphicsContext3D.doSetStereoMode( ((Integer)m[nmesg].args[2]).intValue()); break; case GraphicsContext3D.FLUSH: canvas.graphicsContext3D.doFlush( ((Boolean)m[nmesg].args[2]).booleanValue()); break; case GraphicsContext3D.FLUSH2D: canvas.graphics2D.doFlush(); break; case GraphicsContext3D.DRAWANDFLUSH2D: Object ar[] = m[nmesg].args; canvas.graphics2D.doDrawAndFlushImage( (BufferedImage) ar[2], ((Point) ar[3]).x, ((Point) ar[3]).y, (ImageObserver) ar[4]); break; case GraphicsContext3D.DISPOSE2D: // Issue 583 - the graphics2D field may be null here if (canvas.graphics2D != null) { canvas.graphics2D.doDispose(); } break; case GraphicsContext3D.SET_MODELCLIP: canvas.graphicsContext3D.doSetModelClip( (ModelClip)m[nmesg].args[2]); break; default: break; } } catch (RuntimeException ex) { ex.printStackTrace(); // Issue 260 : indicate fatal error and notify error listeners canvas.setFatalError(); RenderingError err = new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, J3dI18N.getString("Renderer6")); err.setCanvas3D(canvas); err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); notifyErrorListeners(err); } m[nmesg++].decRefcount(); } else { // retained mode rendering long startRenderTime = 0L; if (MasterControl.isStatsLoggable(Level.INFO)) { // Instrumentation of Java 3D renderer startRenderTime = System.nanoTime(); } m[nmesg++].decRefcount(); if (canvas.isFatalError()) { continue; } ImageComponent2DRetained offBufRetained = null; if (renderType == J3dMessage.RENDER_OFFSCREEN) { // Issue 131: set offScreenRendering flag here, since it // otherwise won't be set for auto-off-screen rendering // (which doesn't use renderOffScreenBuffer) canvas.offScreenRendering = true; if (canvas.drawable == null || !canvas.active) { canvas.offScreenRendering = false; continue; } else { offBufRetained = (ImageComponent2DRetained) canvas.offScreenBuffer.retained; if (offBufRetained.isByReference()) { offBufRetained.geomLock.getLock(); } offBufRetained.evaluateExtensions(canvas); } } else if (!canvas.active) { continue; } // Issue 78 - need to get the drawingSurface info every // frame; this is necessary since the HDC (window ID) // on Windows can become invalidated without our // being notified! if (!canvas.offScreen) { canvas.drawingSurfaceObject.getDrawingSurfaceObjectInfo(); } renderBin = canvas.view.renderBin; // setup rendering context // We need to catch NullPointerException when the dsi // gets yanked from us during a remove. if (canvas.useSharedCtx) { if (sharedCtx == null) { sharedCtxDrawable = canvas.drawable; // Always lock for context create if (!canvas.drawingSurfaceObject.renderLock()) { if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; break doneRender; } synchronized (VirtualUniverse.mc.contextCreationLock) { sharedCtx = null; try { sharedCtx = canvas.createNewContext(null, true); } catch (RuntimeException ex) { ex.printStackTrace(); } if (sharedCtx == null) { canvas.drawingSurfaceObject.unLock(); if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; // Issue 260 : indicate fatal error and notify error listeners canvas.setFatalError(); RenderingError err = new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, J3dI18N.getString("Renderer7")); err.setCanvas3D(canvas); err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); notifyErrorListeners(err); break doneRender; } sharedCtxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); needToRebuildDisplayList = true; } canvas.drawingSurfaceObject.unLock(); } } if (canvas.ctx == null) { // Always lock for context create if (!canvas.drawingSurfaceObject.renderLock()) { if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; break doneRender; } synchronized (VirtualUniverse.mc.contextCreationLock) { canvas.ctx = null; try { canvas.ctx = canvas.createNewContext(sharedCtx, false); } catch (RuntimeException ex) { ex.printStackTrace(); } if (canvas.ctx == null) { canvas.drawingSurfaceObject.unLock(); if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; // Issue 260 : indicate fatal error and notify error listeners canvas.setFatalError(); RenderingError err = new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, J3dI18N.getString("Renderer7")); err.setCanvas3D(canvas); err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); notifyErrorListeners(err); break doneRender; } if (canvas.graphics2D != null) { canvas.graphics2D.init(); } canvas.ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); listOfCtxs.add(canvas.ctx); listOfCanvases.add(canvas); if (renderBin.nodeComponentList.size() > 0) { for (i = 0; i < renderBin.nodeComponentList.size(); i++) { NodeComponentRetained nc = (NodeComponentRetained)renderBin.nodeComponentList.get(i); if(nc instanceof ImageComponentRetained) { ((ImageComponentRetained)nc).evaluateExtensions(canvas); } } } // enable separate specular color canvas.enableSeparateSpecularColor(); } // create the cache texture state in canvas // for state download checking purpose if (canvas.texUnitState == null) { canvas.createTexUnitState(); } canvas.resetImmediateRendering(); canvas.drawingSurfaceObject.contextValidated(); if (!canvas.useSharedCtx) { canvas.needToRebuildDisplayList = true; } canvas.drawingSurfaceObject.unLock(); } else { if (canvas.isRunning) { canvas.makeCtxCurrent(); } } if (renderBin != null) { if ((VirtualUniverse.mc.doDsiRenderLock) && (!canvas.drawingSurfaceObject.renderLock())) { if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; break doneRender; } // handle free resource if (canvas.useSharedCtx) { freeResourcesInFreeList(canvas); } else { canvas.freeResourcesInFreeList(canvas.ctx); } if (VirtualUniverse.mc.doDsiRenderLock) { canvas.drawingSurfaceObject.unLock(); } // Issue 109 : removed copyOfCvCache now that we have // a separate canvasViewCache for computing view frustum CanvasViewCache cvCache = canvas.canvasViewCache; // Deadlock if we include updateViewCache in // drawingSurfaceObject sync. canvas.updateViewCache(false, null, null, renderBin.geometryBackground != null); if ((VirtualUniverse.mc.doDsiRenderLock) && (!canvas.drawingSurfaceObject.renderLock())) { if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; break doneRender; } int cvWidth = cvCache.getCanvasWidth(); int cvHeight = cvCache.getCanvasHeight(); // setup viewport canvas.setViewport(canvas.ctx, 0, 0, cvWidth, cvHeight); // rebuild the display list of all dirty renderMolecules. if (canvas.useSharedCtx) { if (needToRebuildDisplayList) { renderBin.updateAllRenderMolecule( this, canvas); needToRebuildDisplayList = false; } if (dirtyDisplayList) { renderBin.updateDirtyDisplayLists(canvas, dirtyRenderMoleculeList, dirtyDlistPerRinfoList, dirtyRenderAtomList,true); dirtyDisplayList = false; } // for shared context, download textures upfront // to minimize the context switching overhead int sz = textureReloadList.size(); if (sz > 0) { for (j = sz-1; j>=0; j--) { textureReloadList.get(j).reloadTextureSharedContext(canvas); } textureReloadList.clear(); } } else { // update each canvas if (canvas.needToRebuildDisplayList) { renderBin.updateAllRenderMolecule(canvas); canvas.needToRebuildDisplayList = false; } if (canvas.dirtyDisplayList) { renderBin.updateDirtyDisplayLists(canvas, canvas.dirtyRenderMoleculeList, canvas.dirtyDlistPerRinfoList, canvas.dirtyRenderAtomList, false); canvas.dirtyDisplayList = false; } } // lighting setup if (canvas.view.localEyeLightingEnable != canvas.ctxEyeLightingEnable) { canvas.ctxUpdateEyeLightingEnable(canvas.ctx, canvas.view.localEyeLightingEnable); canvas.ctxEyeLightingEnable = canvas.view.localEyeLightingEnable; } // stereo setup boolean useStereo = cvCache.getUseStereo(); if (useStereo) { num_stereo_passes = 2; stereo_mode = Canvas3D.FIELD_LEFT; sharedStereoZBuffer = VirtualUniverse.mc.sharedStereoZBuffer; } else { num_stereo_passes = 1; stereo_mode = Canvas3D.FIELD_ALL; // just in case user set flag - // disable since we are not in stereo sharedStereoZBuffer = false; } // full screen anti-aliasing setup if (canvas.view.getSceneAntialiasingEnable() && canvas.sceneAntialiasingAvailable) { if (((canvas.extensionsSupported & Canvas3D.MULTISAMPLE) == 0) || !canvas.sceneAntialiasingMultiSamplesAvailable) { doAccum = true; num_accum_passes = NUM_ACCUMULATION_SAMPLES; System.arraycopy( cvCache.getLeftProjection().mat, 0, accumLeftProj.mat, 0, 16); accumDxFactor = ( canvas.canvasViewCache.getPhysicalWindowWidth() / canvas.canvasViewCache.getCanvasWidth())*canvas.view.fieldOfView; accumDyFactor = ( canvas.canvasViewCache.getPhysicalWindowHeight() / canvas.canvasViewCache.getCanvasHeight())*canvas.view.fieldOfView; accumLeftX = accumLeftProj.mat[3]; accumLeftY = accumLeftProj.mat[7]; if (useStereo) { System.arraycopy( cvCache.getRightProjection().mat, 0, accumRightProj.mat, 0, 16); accumRightX = accumRightProj.mat[3]; accumRightY = accumRightProj.mat[7]; } if (renderBin.geometryBackground != null) { System.arraycopy( cvCache.getInfLeftProjection().mat, 0, accumInfLeftProj.mat, 0, 16); accumInfLeftX = accumInfLeftProj.mat[3]; accumInfLeftY = accumInfLeftProj.mat[7]; if (useStereo) { System.arraycopy( cvCache.getInfRightProjection().mat, 0, accumInfRightProj.mat, 0, 16); accumInfRightX = accumInfRightProj.mat[3]; accumInfRightY = accumInfRightProj.mat[7]; } } } else { if (!canvas.isAntialiasingSet()) { // System.err.println("Renderer : Enable FullSceneAntialiasing"); canvas.setFullSceneAntialiasing(canvas.ctx, true); } } } else { if (canvas.isAntialiasingSet()) { // System.err.println("Renderer : Disable SceneAntialiasing"); canvas.setFullSceneAntialiasing(canvas.ctx, false); } } // background geometry setup if (renderBin.geometryBackground != null) { renderBin.updateInfVworldToVpc(); } // setup default render mode - render to both eyes canvas.setRenderMode(canvas.ctx, Canvas3D.FIELD_ALL, canvas.useDoubleBuffer); // clear background if not full screen antialiasing // and not in stereo mode if (!doAccum && !sharedStereoZBuffer) { BackgroundRetained bg = renderBin.background; canvas.clear(bg, cvWidth, cvHeight); } // handle preRender callback if (VirtualUniverse.mc.doDsiRenderLock) { canvas.drawingSurfaceObject.unLock(); } canvas.view.inCanvasCallback = true; try { canvas.preRender(); } catch (RuntimeException e) { System.err.println("Exception occurred during Canvas3D callback:"); e.printStackTrace(); } catch (Error e) { // Issue 264 - catch Error so Renderer doesn't die System.err.println("Error occurred during Canvas3D callback:"); e.printStackTrace(); } canvas.view.inCanvasCallback = false; if ((VirtualUniverse.mc.doDsiRenderLock) && (!canvas.drawingSurfaceObject.renderLock())) { if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; break doneRender; } // render loop for (pass = 0; pass < num_stereo_passes; pass++) { if (doAccum) { canvas.clearAccum(canvas.ctx); } canvas.setRenderMode(canvas.ctx, stereo_mode, canvas.useDoubleBuffer); for (apass = 0; apass < num_accum_passes; apass++) { // jitter projection matrix and clear background // for full screen anti-aliasing rendering if (doAccum) { accumDx = ACCUM_SAMPLES_X[apass] * accumDxFactor; accumDy = ACCUM_SAMPLES_Y[apass] * accumDyFactor; accumLeftProj.mat[3] = accumLeftX + accumLeftProj.mat[0] * accumDx + accumLeftProj.mat[1] * accumDy; accumLeftProj.mat[7] = accumLeftY + accumLeftProj.mat[4] * accumDx + accumLeftProj.mat[5] * accumDy; if (useStereo) { accumRightProj.mat[3] = accumRightX + accumRightProj.mat[0] * accumDx + accumRightProj.mat[1] * accumDy; accumRightProj.mat[7] = accumRightY + accumRightProj.mat[4] * accumDx + accumRightProj.mat[5] * accumDy; } if (renderBin.geometryBackground != null) { accumInfLeftProj.mat[3] = accumInfLeftX + accumInfLeftProj.mat[0] * accumDx + accumInfLeftProj.mat[1] * accumDy; accumInfLeftProj.mat[7] = accumInfLeftY + accumInfLeftProj.mat[4] * accumDx + accumInfLeftProj.mat[5] * accumDy; if (useStereo) { accumInfRightProj.mat[3] = accumInfRightX + accumInfRightProj.mat[0] * accumDx + accumInfRightProj.mat[1] * accumDy; accumInfRightProj.mat[7] = accumInfRightY + accumInfRightProj.mat[4] * accumDx + accumInfRightProj.mat[5] * accumDy; } } } // clear background for stereo and // accumulation buffer cases if (doAccum || sharedStereoZBuffer) { BackgroundRetained bg = renderBin.background; canvas.clear(bg, cvWidth, cvHeight); } // render background geometry if (renderBin.geometryBackground != null) { // setup rendering matrices if (pass == 0) { canvas.vpcToEc = cvCache.getInfLeftVpcToEc(); if (doAccum) { canvas.setProjectionMatrix( canvas.ctx, accumInfLeftProj); } else { canvas.setProjectionMatrix( canvas.ctx, cvCache.getInfLeftProjection()); } } else { canvas.vpcToEc = cvCache.getInfRightVpcToEc(); if (doAccum) { canvas.setProjectionMatrix( canvas.ctx, accumInfRightProj); } else { canvas.setProjectionMatrix( canvas.ctx, cvCache.getInfRightProjection()); } } canvas.vworldToEc.mul(canvas.vpcToEc, cvCache.getInfVworldToVpc()); // render background geometry renderBin.renderBackground(canvas); } // setup rendering matrices if (pass == 0) { canvas.vpcToEc = cvCache.getLeftVpcToEc(); if (doAccum) { canvas.setProjectionMatrix(canvas.ctx, accumLeftProj); } else { canvas.setProjectionMatrix(canvas.ctx, cvCache.getLeftProjection()); } } else { canvas.vpcToEc = cvCache.getRightVpcToEc(); if (doAccum) { canvas.setProjectionMatrix( canvas.ctx, accumRightProj); } else { canvas.setProjectionMatrix(canvas.ctx, cvCache.getRightProjection()); } } canvas.vworldToEc.mul(canvas.vpcToEc, cvCache.getVworldToVpc()); synchronized (cvCache) { if (pass == 0) { canvas.setFrustumPlanes(cvCache.getLeftFrustumPlanesInVworld()); } else { canvas.setFrustumPlanes(cvCache.getRightFrustumPlanesInVworld()); } } // Force view matrix dirty for each eye. if (useStereo) { canvas.canvasDirty |= Canvas3D.VIEW_MATRIX_DIRTY; } // render opaque geometry renderBin.renderOpaque(canvas); // render ordered geometry renderBin.renderOrdered(canvas); // handle renderField callback if (VirtualUniverse.mc.doDsiRenderLock) { canvas.drawingSurfaceObject.unLock(); } canvas.view.inCanvasCallback = true; try { canvas.renderField(stereo_mode); } catch (RuntimeException e) { System.err.println("Exception occurred during Canvas3D callback:"); e.printStackTrace(); } catch (Error e) { // Issue 264 - catch Error so Renderer doesn't die System.err.println("Error occurred during Canvas3D callback:"); e.printStackTrace(); } canvas.view.inCanvasCallback = false; if ((VirtualUniverse.mc.doDsiRenderLock) && (!canvas.drawingSurfaceObject.renderLock())) { if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.offScreenRendering = false; break doneRender; } // render transparent geometry renderBin.renderTransparent(canvas); if (doAccum) canvas.accum(canvas.ctx, accumValue); } if (doAccum) canvas.accumReturn(canvas.ctx); if (useStereo) { stereo_mode = Canvas3D.FIELD_RIGHT; canvas.rightStereoPass = true; } } canvas.imageReady = true; canvas.rightStereoPass = false; // reset renderMode canvas.setRenderMode(canvas.ctx, Canvas3D.FIELD_ALL, canvas.useDoubleBuffer); // handle postRender callback if (VirtualUniverse.mc.doDsiRenderLock) { canvas.drawingSurfaceObject.unLock(); } canvas.view.inCanvasCallback = true; try { canvas.postRender(); } catch (RuntimeException e) { System.err.println("Exception occurred during Canvas3D callback:"); e.printStackTrace(); } catch (Error e) { // Issue 264 - catch Error so Renderer doesn't die System.err.println("Error occurred during Canvas3D callback:"); e.printStackTrace(); } canvas.view.inCanvasCallback = false; // end offscreen rendering if (canvas.offScreenRendering) { canvas.syncRender(canvas.ctx, true); canvas.endOffScreenRendering(); canvas.offScreenRendering = false; // Issue 489 - don't call postSwap here for auto-offscreen, // since it will be called later by the SWAP operation if (canvas.manualRendering) { // do the postSwap for offscreen here canvas.view.inCanvasCallback = true; try { canvas.postSwap(); } catch (RuntimeException e) { System.err.println("Exception occurred during Canvas 3D callback:"); e.printStackTrace(); } catch (Error e) { // Issue 264 - catch Error so Renderer doesn't die System.err.println("Error occurred during Canvas3D callback:"); e.printStackTrace(); } if (offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } canvas.view.inCanvasCallback = false; canvas.releaseCtx(); } } if (MasterControl.isStatsLoggable(Level.INFO)) { // Instrumentation of Java 3D renderer long deltaTime = System.nanoTime() - startRenderTime; VirtualUniverse.mc.recordTime(MasterControl.TimeType.RENDER, deltaTime); } } else { // if (renderBin != null) if ((offBufRetained != null) && offBufRetained.isByReference()) { offBufRetained.geomLock.unLock(); } } } } // clear array to prevent memory leaks if (opArg == RENDER) { m[0] = null; } else { Arrays.fill(m, 0, totalMessages, null); } } } catch (NullPointerException ne) { // Print NPE, but otherwise ignore it ne.printStackTrace(); if (canvas != null) { // drawingSurfaceObject will safely ignore // this request if this is not lock before canvas.drawingSurfaceObject.unLock(); } } catch (RuntimeException ex) { ex.printStackTrace(); if (canvas != null) { // drawingSurfaceObject will safely ignore // this request if this is not lock before canvas.drawingSurfaceObject.unLock(); } // Issue 260 : indicate fatal error and notify error listeners canvas.setFatalError(); RenderingError err = new RenderingError(RenderingError.UNEXPECTED_RENDERING_ERROR, J3dI18N.getString("Renderer8")); err.setCanvas3D(canvas); err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); notifyErrorListeners(err); } } // resource clean up @Override void shutdown() { removeAllCtxs(); } @Override void cleanup() { super.cleanup(); renderMessage = new J3dMessage[1]; rendererStructure = new RendererStructure(); bgVworldToVpc = new Transform3D(); sharedCtx = null; sharedCtxTimeStamp = 0; sharedCtxDrawable = null; dirtyRenderMoleculeList.clear(); dirtyRenderAtomList.clear(); dirtyDlistPerRinfoList.clear(); textureIdResourceFreeList.clear(); displayListResourceFreeList.clear(); onScreen = null; offScreen = null; m = null; nmesg = 0; } // This is only invoked from removeCtx()/removeAllCtxs() // with drawingSurface already lock final void makeCtxCurrent(Context sharedCtx, Drawable drawable) { if (sharedCtx != currentCtx || drawable != currentDrawable) { Canvas3D.useCtx(sharedCtx, drawable); /* if(!Canvas3D.useCtx(sharedCtx, display, drawable)) { Thread.dumpStack(); System.err.println("useCtx Fail"); } */ currentCtx = sharedCtx; currentDrawable = drawable; } } // No need to free graphics2d and background if it is from // Canvas3D postRequest() offScreen rendering since the // user thread will not wait for it. Also we can just // reuse it as Canvas3D did not destroy. private void removeCtx(Canvas3D cv, Drawable drawable, Context ctx, boolean resetCtx, boolean freeBackground, boolean destroyOffScreenBuffer) { synchronized (VirtualUniverse.mc.contextCreationLock) { if (ctx != null) { int idx = listOfCtxs.indexOf(ctx); if (idx >= 0) { listOfCtxs.remove(idx); listOfCanvases.remove(idx); // Issue 326 : don't check display variable here if ((drawable != null) && cv.added) { // cv.ctx may reset to -1 here so we // always use the ctx pass in. if (cv.drawingSurfaceObject.renderLock()) { // if it is the last one, free shared resources if (sharedCtx != null) { if (listOfCtxs.isEmpty()) { makeCtxCurrent(sharedCtx, sharedCtxDrawable); freeResourcesInFreeList(null); freeContextResources(); Canvas3D.destroyContext(sharedCtxDrawable, sharedCtx); currentCtx = null; currentDrawable = null; } else { freeResourcesInFreeList(cv); } cv.makeCtxCurrent(ctx, drawable); } else { cv.makeCtxCurrent(ctx, drawable); cv.freeResourcesInFreeList(ctx); } cv.freeContextResources(this, freeBackground, ctx); Canvas3D.destroyContext(drawable, ctx); currentCtx = null; currentDrawable = null; cv.drawingSurfaceObject.unLock(); } } } if (resetCtx) { cv.ctx = null; } if ((sharedCtx != null) && listOfCtxs.isEmpty()) { sharedCtx = null; sharedCtxTimeStamp = 0; } cv.ctxTimeStamp = 0; } // Fix for issue 18. // Since we are now the renderer thread, // we can safely execute destroyOffScreenBuffer. if(destroyOffScreenBuffer) { cv.destroyOffScreenBuffer(ctx, drawable); cv.offScreenBufferPending = false; } } } void removeAllCtxs() { Canvas3D cv; synchronized (VirtualUniverse.mc.contextCreationLock) { for (int i=listOfCanvases.size()-1; i >=0; i--) { cv = listOfCanvases.get(i); if ((cv.screen != null) && (cv.ctx != null)) { // Issue 326 : don't check display variable here if ((cv.drawable != null) && cv.added) { if (cv.drawingSurfaceObject.renderLock()) { // We need to free sharedCtx resource // first before last non-sharedCtx to // workaround Nvidia driver bug under Linux // that crash on freeTexture ID:4685156 if ((i == 0) && (sharedCtx != null)) { makeCtxCurrent(sharedCtx, sharedCtxDrawable); freeResourcesInFreeList(null); freeContextResources(); Canvas3D.destroyContext(sharedCtxDrawable, sharedCtx); currentCtx = null; currentDrawable = null; } cv.makeCtxCurrent(); cv.freeResourcesInFreeList(cv.ctx); cv.freeContextResources(this, true, cv.ctx); Canvas3D.destroyContext(cv.drawable, cv.ctx); currentCtx = null; currentDrawable = null; cv.drawingSurfaceObject.unLock(); } } } cv.ctx = null; cv.ctxTimeStamp = 0; } if (sharedCtx != null) { sharedCtx = null; sharedCtxTimeStamp = 0; } listOfCanvases.clear(); listOfCtxs.clear(); } } // handle free resource in the FreeList void freeResourcesInFreeList(Canvas3D cv) { Iterator it; boolean isFreeTex = (textureIdResourceFreeList.size() > 0); boolean isFreeDL = (displayListResourceFreeList.size() > 0); int val; if (isFreeTex || isFreeDL) { if (cv != null) { cv.makeCtxCurrent(sharedCtx); } if (isFreeDL) { for (it = displayListResourceFreeList.iterator(); it.hasNext();) { val = it.next().intValue(); if (val <= 0) { continue; } Canvas3D.freeDisplayList(sharedCtx, val); } displayListResourceFreeList.clear(); } if (isFreeTex) { for (it = textureIdResourceFreeList.iterator(); it.hasNext();) { val = it.next().intValue(); if (val <= 0) { continue; } if (val >= textureIDResourceTable.size()) { MasterControl.getCoreLogger().severe( "Error in freeResourcesInFreeList : ResourceIDTableSize = " + textureIDResourceTable.size() + " val = " + val); } else { TextureRetained tex = textureIDResourceTable.get(val); if (tex != null) { synchronized (tex.resourceLock) { tex.resourceCreationMask &= ~rendererBit; if (tex.resourceCreationMask == 0) { tex.freeTextureId(val); } } } textureIDResourceTable.set(val, null); } Canvas3D.freeTexture(sharedCtx, val); } textureIdResourceFreeList.clear(); } if (cv != null) { cv.makeCtxCurrent(cv.ctx); } } } final void addTextureResource(int id, TextureRetained obj) { if (textureIDResourceTable.size() <= id) { for (int i=textureIDResourceTable.size(); i < id; i++) { textureIDResourceTable.add(null); } textureIDResourceTable.add(obj); } else { textureIDResourceTable.set(id, obj); } } void freeContextResources() { TextureRetained tex; for (int id = textureIDResourceTable.size()-1; id >= 0; id--) { tex = textureIDResourceTable.get(id); if (tex == null) { continue; } Canvas3D.freeTexture(sharedCtx, id); synchronized (tex.resourceLock) { tex.resourceCreationMask &= ~rendererBit; if (tex.resourceCreationMask == 0) { tex.freeTextureId(id); } } } textureIDResourceTable.clear(); // displayList is free in Canvas.freeContextResources() } /** * Send a message to the notification thread, which will call the * shader error listeners. */ static void notifyErrorListeners(RenderingError err) { J3dNotification notification = new J3dNotification(); notification.type = J3dNotification.RENDERING_ERROR; notification.universe = null;//cv.view.universe; notification.args[0] = err; VirtualUniverse.mc.sendNotification(notification); } // Default rendering error listener class private static RenderingErrorListener defaultErrorListener = null; synchronized static RenderingErrorListener getDefaultErrorListener() { if (defaultErrorListener == null) { defaultErrorListener = new DefaultErrorListener(); } return defaultErrorListener; } static class DefaultErrorListener implements RenderingErrorListener { @Override public void errorOccurred(RenderingError error) { System.err.println(); System.err.println("DefaultRenderingErrorListener.errorOccurred:"); error.printVerbose(); System.exit(1); } } }