Commits

Michael Ludwig committed b8a4407

Add various new render modes for the jogl renderer. Remove debugging statements from DDSTexture.

  • Participants
  • Parent commits d5e7377

Comments (0)

Files changed (8)

src/com/ferox/renderer/impl/jogl/AttachableSurfaceImplHelper.java

 package com.ferox.renderer.impl.jogl;
 
+import java.awt.EventQueue;
 import java.util.ArrayList;
 import java.util.List;
 
 import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLContext;
+import javax.media.opengl.Threading;
 
 import com.ferox.renderer.RenderException;
 
  *
  */
 public class AttachableSurfaceImplHelper {
+	/*
+	 * Various supported render modes. There are 3 varieties, of 2 different
+	 * types. The first type is forcing JOGL rendering onto another thread (the
+	 * AWT EDT). The second is active rendering, which renders on the current
+	 * thread.
+	 * 
+	 * The 1st has 2 varieties, the standard way, and an experimental way using
+	 * invokeLater() instead. They both perform about the same. In small scenes
+	 * they suffer from unstable high frame rates.
+	 * 
+	 * Active rendering is significantly faster, and has more stable framerates.
+	 * Unfortunately, it suffers from rendering artifacts in Mac from other
+	 * windows. I don't know why. It is still suitable for fullscreen rendering.
+	 */
+	private static final int RENDER_MODE_JOGL_STANDARD = 0;
+	private static final int RENDER_MODE_AWT_WAIT = 1;
+	private static final int RENDER_MODE_ACTIVE = 2;
+	
+	private static final int renderMode = RENDER_MODE_JOGL_STANDARD;
+	
+	/* Variables used for each render mode. */
 	private List<JoglRenderSurface> attachedSurfaces;
 	private Runnable resourceAction;
+	private Exception caughtEDTException;
 	
-	private Exception caughtEDTException;
-
+	/* Variables used for AWT_WAIT. */
+	private InvokeLaterRenderAction invokeLaterAction;
+	private volatile boolean actionFinished; // signal back to other thread
+	private Object lock;
+	
 	public AttachableSurfaceImplHelper() {
 		this.attachedSurfaces = new ArrayList<JoglRenderSurface>();
 	}
 	
-	/** Matches the render(drawable) method of the AttachableSurfaceGLEventListener. */
-	public void render(GLAutoDrawable drawable) throws RenderException {
-		try {
-			this.caughtEDTException = null;
-			drawable.display();
-			
-			Exception c = this.caughtEDTException;
-			if (c != null)
-				throw new RenderException(c);
-		} finally {
-			this.attachedSurfaces.clear();
-			this.resourceAction = null;
-			this.caughtEDTException = null;
-		}
-	}
-	
 	/** Matches the assignResourceAction() method of the AttachableSurfaceGLEventListener. */
 	public void assignResourceAction(Runnable action) {
 		this.resourceAction = action;
 			return;
 		}
 	}
+	
+	/** Matches the render(drawable) method of the AttachableSurfaceGLEventListener. */
+	public void render(final GLAutoDrawable drawable) throws RenderException {
+		try {
+			this.caughtEDTException = null;
+			
+			switch(renderMode) {
+			case RENDER_MODE_ACTIVE:
+				this.renderActive(drawable);
+				break;
+			case RENDER_MODE_AWT_WAIT:
+				this.renderAwtWait(drawable);
+				break;
+			case RENDER_MODE_JOGL_STANDARD:
+				this.renderJoglStandard(drawable);
+				break;
+			}
+			
+			Exception c = this.caughtEDTException;
+			if (c != null)
+				throw new RenderException(c);
+		} finally {
+			this.attachedSurfaces.clear();
+			this.resourceAction = null;
+			this.caughtEDTException = null;
+		}
+	}
+	
+	/* Render operation for the RENDER_MODE_JOGL_STANDARD. */
+	private void renderJoglStandard(GLAutoDrawable drawable) {
+		drawable.display();
+	}
+	
+	/* Render operation for the RENDER_MODE_AWT_WAIT. */
+	private void renderAwtWait(GLAutoDrawable drawable) {
+		if (this.lock == null) {
+			// lock and invokeLaterAction are null/not-null at the same time
+			this.lock = new Object();
+			this.invokeLaterAction = new InvokeLaterRenderAction();
+		}
+		
+		// reset for rendering this drawable
+		this.actionFinished = false;
+		this.invokeLaterAction.drawable = drawable;
+		
+		// run the action
+		EventQueue.invokeLater(this.invokeLaterAction);
+		
+		// wait to be notified that it has finished
+		try {
+			synchronized(this.lock) {
+				while(!this.actionFinished)
+					this.lock.wait();
+			}
+		} catch (InterruptedException ie) {
+			// Do nothing ??
+		}
+	}
+	
+	/* Render operation for the RENDER_MODE_ACTIVE. */
+	private void renderActive(GLAutoDrawable drawable) {
+		// must disable single threading
+		if (Threading.isSingleThreaded())
+			Threading.disableSingleThreading();
+		
+		// swap buffers manually
+		if (drawable.getAutoSwapBufferMode())
+			drawable.setAutoSwapBufferMode(false);
+		
+		GLContext context = drawable.getContext();
+		context.makeCurrent();
+		this.display(drawable);
+		context.release();
+		drawable.swapBuffers();
+	}
+	
+	/* Internal class used for renderAwtWait(). */
+	private class InvokeLaterRenderAction implements Runnable {
+		GLAutoDrawable drawable;
+		
+		public void run() {
+			synchronized(lock) {
+				this.drawable.display();
+				
+				// notify calling thread that we're done
+				actionFinished = true;
+				lock.notifyAll();
+			}
+		}
+	}
 }

src/com/ferox/renderer/impl/jogl/JoglCapabilitiesDetector.java

 import javax.media.opengl.GLCapabilities;
 import javax.media.opengl.GLDrawableFactory;
 import javax.media.opengl.GLPbuffer;
-import javax.swing.SwingUtilities;
 
 import com.ferox.renderer.RenderCapabilities;
 import com.ferox.renderer.impl.CapabilitiesDetector;
 				pbuffer.destroy();
 			} else {
 				// quick make a frame visible to get a GLCanvas
-				try {
-					SwingUtilities.invokeAndWait(new Runnable() {
-						@Override
-						public void run() {
-							// setup
-							GLCanvas canvas = new GLCanvas();
-							
-							Frame frame = new Frame();
-							frame.add(canvas);
-							frame.setSize(1, 1);
-							
-							// detect
-							frame.setVisible(true);
-							canvas.getContext().makeCurrent();
-							JoglCapabilitiesDetector.this.queryCapabilities(canvas.getGL());
-							canvas.getContext().release();
-							
-							// clean-up
-							frame.setVisible(false);
-							canvas.getContext().destroy();
-							frame.dispose();
-						}
-					});
-				} catch (Exception e) {
-					throw new RuntimeException("Unnable to detect RenderCapabilities", e);
-				}
+				JoglSurfaceFactory.invokeOnAwtThread(new Runnable() {
+					@Override
+					public void run() {
+						// setup
+						GLCanvas canvas = new GLCanvas();
+
+						Frame frame = new Frame();
+						frame.add(canvas);
+						frame.setSize(1, 1);
+
+						// detect
+						frame.setVisible(true);
+						canvas.getContext().makeCurrent();
+						JoglCapabilitiesDetector.this.queryCapabilities(canvas.getGL());
+						canvas.getContext().release();
+
+						// clean-up
+						frame.setVisible(false);
+						canvas.getContext().destroy();
+						frame.dispose();
+					}
+				});
 			}
 		}
 		

src/com/ferox/renderer/impl/jogl/JoglOnscreenSurface.java

 
 import com.ferox.renderer.DisplayOptions;
 import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.RenderException;
 import com.ferox.renderer.DisplayOptions.AntiAliasMode;
 import com.ferox.renderer.DisplayOptions.DepthFormat;
 import com.ferox.renderer.DisplayOptions.PixelFormat;
 		this.canvas = new GLCanvas(chooseCapabilities(optionsRequest), new DefaultGLCapabilitiesChooser(), factory.getShadowContext(), null);
 		this.frame = new Frame();
 
-		try {
-			SwingUtilities.invokeAndWait(new Runnable() {
-				public void run() {
-					frame.setResizable(resizable);
-					frame.setUndecorated(undecorated);
-					frame.setBounds(x, y, Math.max(width, 1), Math.max(height, 1));
+		JoglSurfaceFactory.invokeOnAwtThread(new Runnable() {
+			public void run() {
+				frame.setResizable(resizable);
+				frame.setUndecorated(undecorated);
+				frame.setBounds(x, y, Math.max(width, 1), Math.max(height, 1));
 
-					frame.add(canvas);
+				frame.add(canvas);
 
-					frame.setVisible(true);
-					canvas.requestFocusInWindow();
-				}
-			});
-		} catch (Exception e) {
-			throw new RenderException("Error creating JoglOnscreenSurface", e);
-		}
+				frame.setVisible(true);
+				canvas.requestFocusInWindow();
+			}
+		});
 
 		this.frame.addWindowListener(this);
 		
 		this.canvas.addGLEventListener(this);
+		this.frame.setIgnoreRepaint(true);
 		this.canvas.setIgnoreRepaint(true);
 		
 		this.record = new JoglStateRecord(factory.getRenderer().getCapabilities());
 	public void destroySurface() {
 		this.frame.removeWindowListener(JoglOnscreenSurface.this);
 
-		try {
-			SwingUtilities.invokeAndWait(new Runnable() {
-				public void run() {
-					frame.setVisible(false);
-					frame.dispose();
-				}
-			});
-		} catch (Exception e) {
-			throw new RenderException("Error hiding JoglWindowSurface", e);
-		}
+		JoglSurfaceFactory.invokeOnAwtThread(new Runnable() {
+			public void run() {
+				frame.setVisible(false);
+				frame.dispose();
+			}
+		});
 		
 		this.canvas.getContext().destroy();
 		super.destroySurface();

src/com/ferox/renderer/impl/jogl/JoglSurfaceFactory.java

 package com.ferox.renderer.impl.jogl;
 
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import javax.media.opengl.GLAutoDrawable;
 import javax.media.opengl.GLContext;
 import javax.media.opengl.Threading;
+import javax.swing.SwingUtilities;
 
 import com.ferox.math.Transform;
 import com.ferox.renderer.DisplayOptions;
 		return this.shadowContext.getContext();
 	}
 	
+	/** Utility method to invoke a Runnable on the AWT event dispatch
+	 * thread (e.g. for modifying AWT and Swing components). 
+	 * 
+	 * This will throw an runtime exception if a problem occurs.
+	 * It works properly if called from the AWT thread. 
+	 * 
+	 * This should be used when EventQueue.invokeAndWait() or
+	 * SwingUtilities.invokeAndWait() would be used, except that
+	 * this is thread safe. */
+	public static void invokeOnAwtThread(Runnable r) {
+		if (SwingUtilities.isEventDispatchThread()) {
+			r.run();
+		} else {
+			try {
+				SwingUtilities.invokeAndWait(r);
+			} catch (InterruptedException e) {
+				throw new RuntimeException(e);
+			} catch (InvocationTargetException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+	
 	private void relinquishSurface(JoglRenderSurface surface) {
 		GLAutoDrawable c = surface.getGLAutoDrawable();
 		if (c != null) {

src/com/ferox/renderer/impl/jogl/OnscreenShadowContext.java

 import javax.media.opengl.GLAutoDrawable;
 import javax.media.opengl.GLCanvas;
 import javax.media.opengl.GLContext;
-import javax.swing.SwingUtilities;
 
 import com.ferox.renderer.RenderCapabilities;
 import com.ferox.renderer.RenderException;
 	public void render() throws RenderException {
 		// must make the frame visible so that the context is valid
 		// for gl execution (instead of just context sharing)
-		try {
-			SwingUtilities.invokeAndWait(this.showFrame);
-		} catch (Exception e) {
-			throw new RenderException("Unable to make a frame visible for shadow context", e);
-		}
+		JoglSurfaceFactory.invokeOnAwtThread(this.showFrame);
 		
 		this.record = new JoglStateRecord(this.caps);
 		
 			super.render();
 		} finally {
 			// must always hide the frame, even when an exception is thrown
-			try {
-				SwingUtilities.invokeAndWait(this.hideFrame);
-			} catch (Exception e) { /* Do nothing ?? */}
+			JoglSurfaceFactory.invokeOnAwtThread(this.hideFrame);
 		}
 	}
 	
 	@Override
 	public void destroy() {
 		if (this.frame != null) {
-			try {
-				SwingUtilities.invokeAndWait(new Runnable() {
-					public void run() {
-						OnscreenShadowContext.this.frame.setVisible(false);
-						OnscreenShadowContext.this.frame.dispose();
-					}
-				});
-			} catch (Exception e) { /* Do nothing ?? */	}
+			JoglSurfaceFactory.invokeOnAwtThread(new Runnable() {
+				public void run() {
+					OnscreenShadowContext.this.frame.setVisible(false);
+					OnscreenShadowContext.this.frame.dispose();
+				}
+			});
 			this.frame = null;
 		}
 		

src/com/ferox/resource/texture/loader/DDSTexture.java

 	 * where the next byte read is the first byte in the texture data (header already read from stream). */
 	private void readData(InputStream in) throws IOException {
 		int width, height, depth, size;
-		System.out.println(this.target + " " + this.format + " " + this.type);
-		System.out.println(this.header.pixelFormat);
-		System.out.println(this.mipmapCount + " " + this.width + " " + this.height);
-		
 		int arrayCount = 1;
 		if (this.target == TextureTarget.T_CUBEMAP)
 			arrayCount = 6; // faces are ordered px, nx, py, ny, pz, nz

test/com/ferox/BasicApplication.java

 	protected SceneElement scene;
 	
 	private long lastFpsUpdate;
+	private int fpsCount;
+	private float avgFps;
 	
 	private StringBuilder fpsStringBuilder;
 	private Formatter fpsFormatter;
 		
 		this.pass = new BasicRenderPass(null, v, this.createQueue(), false);
 		
-		//this.window = renderer.createFullscreenSurface(new DisplayOptions(), 1440, 900);
+		//this.window = renderer.createFullscreenSurface(new DisplayOptions(), 640, 480);
 		this.window = renderer.createWindowSurface(this.createOptions(), 10, 10, 640, 480, false, false);
 		this.window.addRenderPass(this.pass);
 		this.window.setTitle(this.getClass().getSimpleName());
 			renderer.queueRender(this.window);
 			boolean res = super.render(renderer);
 			
+			this.fpsCount++;
+			this.avgFps += this.stats.getFramesPerSecond();
+			
 			if (System.currentTimeMillis() - this.lastFpsUpdate > UPDATE_TIME) {
 				this.lastFpsUpdate = System.currentTimeMillis();
 				Runtime run = Runtime.getRuntime();
 				this.fpsStringBuilder.setLength(0);
-				this.fpsFormatter.format("FPS: %.2f\nMeshes: %d\nPolygons: %d\nUsed: %.2f / %.2f M", this.stats.getFramesPerSecond(), 
+				this.fpsFormatter.format("FPS: %.2f\nMeshes: %d\nPolygons: %d\nUsed: %.2f / %.2f M", this.avgFps / this.fpsCount, 
 																									 this.stats.getMeshCount(),
 																									 this.stats.getPolygonCount(),
 																									 (run.totalMemory() - run.freeMemory()) / 1e6f,
 																									 run.totalMemory() / 1e6f);
 				this.fpsText.setText(this.fpsStringBuilder.toString());
 				renderer.requestUpdate(this.fpsText, true);
+				
+				this.fpsCount = 0;
+				this.avgFps = 0;
 			}
 			
 			return res;

test/com/ferox/scene/RttCubeTest.java

 
 public class RttCubeTest extends BasicApplication {
 	public static final boolean DEBUG = false;
-	public static final boolean USE_VBO = false;
-	public static final boolean RANDOM_PLACEMENT = false;
+	public static final boolean USE_VBO = true;
+	public static final boolean RANDOM_PLACEMENT = true;
 	
 	public static final int NUM_CUBES = 10000;
 	public static final int BOUNDS = 100;
 		for (Appearance a: apps)
 			a.addState(ps);
 		
-		if (USE_VBO)
+		if (USE_VBO) {
 			this.geom = new VertexBufferGeometry(new Box(2f).requestVboUpdate(renderer, true));
-		else
+		} else
 			this.geom = new VertexArrayGeometry(new Box(2f));
 		renderer.requestUpdate(this.geom, true);
 		
 		this.window.addRenderPass(new BasicRenderPass(colorShape, ortho));
 		this.window.addRenderPass(new BasicRenderPass(depthShape, ortho));
 		
+		this.window.setColorBufferCleared(false);
+		this.window.setDepthBufferCleared(false);
+		
 		return sceneDepth;
 	}