Michael Ludwig avatar Michael Ludwig committed c334bb7

Improve lighting model interface for FFP renderer.
Improve/fix destroyed resource handling in renderers.
Improve capabilities detection and GL profile selection.
Change DestructibleManager to use PhantomReferences.
Move renderer activation into a new interface, and have activation be handled by HardwareAccessLayer so that only the used renderer is reset or activated.

Comments (0)

Files changed (27)

ferox-renderer-jogl/src/main/java/com/ferox/renderer/impl/jogl/JoglCapabilities.java

 import com.jogamp.newt.NewtFactory;
 import com.jogamp.newt.Window;
 
+import javax.media.nativewindow.NativeWindowFactory;
 import javax.media.opengl.*;
 import java.util.Arrays;
 import java.util.Collections;
 
     private boolean queried = false;
 
-    public static JoglCapabilities computeCapabilities(GLProfile profile, DisplayMode[] availableModes,
-                                                       boolean forceNoPBuffer, boolean forceNoFBO) {
+    public static JoglCapabilities computeCapabilities(GLProfile profile, DisplayMode[] availableModes) {
+        boolean forceNoPBuffer = Boolean.getBoolean("ferox.disable.pbuffer");
+        boolean forceNoFBO = Boolean.getBoolean("ferox.disable.fbo");
+
+        if (NativeWindowFactory.getNativeWindowType(true).equals(NativeWindowFactory.TYPE_MACOSX) &&
+            profile.isGL3()) {
+            // newer versions of Mac OS X have deprecated pbuffers, making them unreliable
+            forceNoPBuffer = true;
+        }
+
         GLDrawableFactory factory = GLDrawableFactory.getFactory(profile);
 
         JoglCapabilities caps = new JoglCapabilities();
         caps.availableModes = availableModes;
+
         caps.pbuffersSupported = !forceNoPBuffer && factory.canCreateGLPbuffer(factory.getDefaultDevice());
 
         // for loop over guessed common msaa, depth, and stencil options
 
     private static boolean isValid(GLCapabilities format, JoglCapabilities caps, GLDrawableFactory factory,
                                    boolean forceNoFBOs) {
-        if (caps.pbuffersSupported) {
+        boolean canUseFBO = factory.canCreateFBO(factory.getDefaultDevice(), format.getGLProfile());
+        if (caps.pbuffersSupported || canUseFBO) {
             try {
-                format.setFBO(false);
-                format.setPBuffer(true);
+                // if pbuffers are available use them for proper offscreen, since it looks like the FBO
+                // fallback creates a 1x1 window, but is faster than the explicit creation we do below
+                format.setFBO(!caps.pbuffersSupported);
+                format.setPBuffer(caps.pbuffersSupported);
                 format.setOnscreen(false);
 
                 GLOffscreenAutoDrawable offscreen = factory
 
                 return true;
             } catch (GLException e) {
+                e.printStackTrace();
                 return false;
             }
         } else {
 
                 return true;
             } catch (GLException e) {
+                e.printStackTrace();
                 return false;
             }
         }
         float glslVersionNum = formatVersion(gl.glGetString(GL2.GL_SHADING_LANGUAGE_VERSION));
         glslVersion = (int) Math.floor(100 * glslVersionNum);
 
-        // FIXME do extensions start with GL_ or go straight into EXT_ or ARB_?
         geometryShaderSupport = gl.isExtensionAvailable("GL_EXT_geometry_shader4") ||
                                 (majorVersion >= 3 && minorVersion >= 3);
 
         supportsMultipleOnscreenSurfaces = false;
 
         if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) {
-            gl.glGetIntegerv(GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, query, 0);
+            gl.glGetIntegerv(GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, query, 0);
             maxAnisoLevel = query[0];
         } else {
             maxAnisoLevel = 0;

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/LwjglCapabilities.java

 
     private boolean queried = false;
 
-    public static LwjglCapabilities computeCapabilities(DisplayMode[] availableModes, boolean forceNoPBuffer,
-                                                        boolean forceNoFBO) {
+    public static LwjglCapabilities computeCapabilities(ContextAttribs attribs,
+                                                        DisplayMode[] availableModes) {
+        boolean forceNoPBuffer = Boolean.getBoolean("ferox.disable.pbuffer");
+        boolean forceNoFBO = Boolean.getBoolean("ferox.disable.fbo");
+
+        // Technically on Mac 10.7+ pbuffers are deprecated but LWJGL does not support sharing contexts
+        // between AWTGLCanvas and Display. That means there's no real alternative. Also, unlike JOGL
+        // LWJGL seems to create valid enough contexts to get away with not forcing pbuffers to false
+
         LwjglCapabilities caps = new LwjglCapabilities();
         caps.availableModes = availableModes;
         caps.pbuffersSupported =
         List<Integer> validMSAA = new ArrayList<>();
 
         for (int depth : GUESSED_DEPTH_BITS) {
-            if (isValid(baseFormat.withDepthBits(depth), caps, forceNoFBO)) {
+            if (isValid(attribs, baseFormat.withDepthBits(depth), caps, forceNoFBO)) {
                 validDepth.add(depth);
             }
         }
         for (int stencil : GUESSED_STENCIL_BITS) {
-            if (isValid(baseFormat.withStencilBits(stencil), caps, forceNoFBO)) {
+            if (isValid(attribs, baseFormat.withStencilBits(stencil), caps, forceNoFBO)) {
                 validStencil.add(stencil);
             }
         }
         for (int msaa : GUESSED_MSAA_COUNTS) {
-            if (isValid(baseFormat.withSamples(msaa), caps, forceNoFBO)) {
+            if (isValid(attribs, baseFormat.withSamples(msaa), caps, forceNoFBO)) {
                 validMSAA.add(msaa);
             }
         }
         return caps;
     }
 
-    private static boolean isValid(PixelFormat format, LwjglCapabilities caps, boolean forceNoFBOs) {
+    private static boolean isValid(ContextAttribs attribs, PixelFormat format, LwjglCapabilities caps,
+                                   boolean forceNoFBOs) {
         if (caps.pbuffersSupported) {
             try {
-                Pbuffer pbuffer = new Pbuffer(1, 1, format, null);
+                Pbuffer pbuffer = new Pbuffer(1, 1, format, null, null, attribs);
                 pbuffer.makeCurrent();
                 if (!caps.queried) {
                     caps.queryWithContext(forceNoFBOs);
         } else {
             try {
                 Display.setDisplayMode(new org.lwjgl.opengl.DisplayMode(1, 1));
-                Display.create(format);
+                Display.create(format, attribs);
                 Display.makeCurrent();
                 if (!caps.queried) {
                     caps.queryWithContext(forceNoFBOs);

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/LwjglContext.java

 import com.ferox.renderer.FrameworkException;
 import com.ferox.renderer.GlslRenderer;
 import com.ferox.renderer.impl.OpenGLContext;
+import com.ferox.renderer.impl.ShaderFixedFunctionEmulator;
 import com.ferox.renderer.impl.SharedState;
 import com.ferox.renderer.impl.resources.BufferImpl;
 import com.ferox.renderer.impl.resources.ShaderImpl;
     private final List<Runnable> cleanupTasks;
 
     private final SharedState sharedState;
-    private final LwjglFixedFunctionRenderer fixed;
+    private final FixedFunctionRenderer fixed;
     private final LwjglGlslRenderer glsl;
 
     private int fbo;
         sharedState = new SharedState(caps.getMaxFragmentShaderTextures());
 
         LwjglRendererDelegate shared = new LwjglRendererDelegate(this, sharedState);
+        glsl = new LwjglGlslRenderer(this, shared, caps.getMaxVertexAttributes());
+
         if (caps.getMajorVersion() < 3) {
             fixed = new LwjglFixedFunctionRenderer(this, shared);
         } else {
-            throw new UnsupportedOperationException(
-                    "No emulation shader written yet, can't support FFP renderer");
+            fixed = new ShaderFixedFunctionEmulator(glsl);
         }
-        glsl = new LwjglGlslRenderer(this, shared, caps.getMaxVertexAttributes());
     }
 
     /**
 
     @Override
     public void bindArrayVBO(BufferImpl.BufferHandle vbo) {
-        if (vbo.isDestroyed()) {
+        if (vbo != null && vbo.isDestroyed()) {
             vbo = null;
         }
 
 
     @Override
     public void bindElementVBO(BufferImpl.BufferHandle vbo) {
-        if (vbo.isDestroyed()) {
+        if (vbo != null && vbo.isDestroyed()) {
             vbo = null;
         }
 
 
     @Override
     public void bindShader(ShaderImpl.ShaderHandle shader) {
-        if (shader.isDestroyed()) {
+        if (shader != null && shader.isDestroyed()) {
             shader = null;
         }
 
 
     @Override
     public void bindTexture(int textureUnit, TextureImpl.TextureHandle texture) {
-        if (texture.isDestroyed()) {
+        if (texture != null && texture.isDestroyed()) {
             texture = null;
         }
 

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/LwjglFixedFunctionRenderer.java

 
     // state tracking
     private boolean alphaTestEnabled;
+    private boolean twosidedLighting;
 
     public LwjglFixedFunctionRenderer(LwjglContext context, LwjglRendererDelegate delegate) {
         super(context, delegate);
 
         transferBuffer = BufferUtil.newByteBuffer(DataType.FLOAT, 16).asFloatBuffer();
         alphaTestEnabled = false;
+        twosidedLighting = false;
     }
 
     @Override
 
             // for the lighting model, we use local viewer and separate specular color, smooth shading, and
             // two sided lighting
-            GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
             GL11.glLightModeli(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_TRUE);
             GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR);
             GL11.glShadeModel(GL11.GL_SMOOTH);
     }
 
     @Override
+    public void setDrawStyle(DrawStyle front, DrawStyle back) {
+        super.setDrawStyle(front, back);
+
+        if (back != DrawStyle.NONE && !twosidedLighting) {
+            GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
+            twosidedLighting = true;
+        } else if (back == DrawStyle.NONE && twosidedLighting) {
+            GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_FALSE);
+            twosidedLighting = false;
+        }
+    }
+
+    @Override
     protected void glMatrixMode(MatrixMode mode) {
         switch (mode) {
         case MODELVIEW:

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/LwjglRendererDelegate.java

     // capabilities
     private boolean initialized;
 
-    // state tracking for buffer clearing
-    private final Vector4 clearColor = new Vector4(0f, 0f, 0f, 0f);
-    private double clearDepth = 1;
-    private int clearStencil = 0;
-
-    // state tracking for draw styles
-    private boolean cullEnabled = true;
-    private int frontPolyMode = GL11.GL_FILL;
-    private int backPolyMode = GL11.GL_FILL;
-
     /**
      * Create a new delegate that renders for the given context. The SharedState must be the same shared state
      * instance used by all renderers for the given context (so that it is shared).
 
         if (cullFace == 0) {
             // to show both sides, must disable culling
-            if (cullEnabled) {
-                cullEnabled = false;
-                glEnable(GL11.GL_CULL_FACE, false);
-            }
+            glEnable(GL11.GL_CULL_FACE, false);
         } else {
-            if (!cullEnabled) {
-                cullEnabled = true;
-                glEnable(GL11.GL_CULL_FACE, true);
-            }
+            glEnable(GL11.GL_CULL_FACE, true);
             GL11.glCullFace(cullFace);
         }
 
         if (front != DrawStyle.NONE) {
-            int frontMode = Utils.getGLPolygonMode(front);
-            if (frontPolyMode != frontMode) {
-                frontPolyMode = frontMode;
-                GL11.glPolygonMode(GL11.GL_FRONT, frontMode);
-            }
+            GL11.glPolygonMode(GL11.GL_FRONT, Utils.getGLPolygonMode(front));
         }
 
         if (back != DrawStyle.NONE) {
-            int backMode = Utils.getGLPolygonMode(back);
-            if (backPolyMode != backMode) {
-                backPolyMode = backMode;
-                GL11.glPolygonMode(GL11.GL_BACK, backMode);
-            }
+            GL11.glPolygonMode(GL11.GL_BACK, Utils.getGLPolygonMode(back));
         }
     }
 
             throw new IllegalArgumentException("Clear depth must be in [0, 1], not: " + depth);
         }
 
-        if (!this.clearColor.equals(color)) {
-            this.clearColor.set(color);
-            GL11.glClearColor((float) color.x, (float) color.y, (float) color.z, (float) color.w);
-        }
-        if (this.clearDepth != depth) {
-            this.clearDepth = depth;
-            GL11.glClearDepth(depth);
-        }
-        if (this.clearStencil != stencil) {
-            this.clearStencil = stencil;
-            GL11.glClearStencil(stencil);
-        }
+        GL11.glClearColor((float) color.x, (float) color.y, (float) color.z, (float) color.w);
+        GL11.glClearDepth(depth);
+        GL11.glClearStencil(stencil);
 
         int clearBits = 0;
         if (clearColor) {

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/LwjglStaticDisplaySurface.java

             org.lwjgl.opengl.DisplayMode lwjglMode = factory.getLWJGLDisplayMode(options.getFullscreenMode());
             try {
                 Display.setDisplayModeAndFullscreen(lwjglMode);
-                Display.create(format, realShare);
+                Display.create(format, realShare, factory.getContextAttribs());
             } catch (LWJGLException e) {
                 if (Display.isCreated()) {
                     Display.destroy();
 
             try {
                 Display.setParent(innerCanvas);
-                Display.create(format, realShare);
+                Display.create(format, realShare, factory.getContextAttribs());
             } catch (LWJGLException e) {
                 if (Display.isCreated()) {
                     Display.destroy();
             displayMode = factory.getDefaultDisplayMode();
         }
 
+        // OpenGL3.0 gets rid of STENCIL_BITS and DEPTH_BITS queries
+        stencilBits = options.getStencilBufferBits();
+        depthBits = options.getDepthBufferBits();
+
         // Detect buffer config while the context is current
-        stencilBits = GL11.glGetInteger(GL11.GL_STENCIL_BITS);
-        depthBits = GL11.glGetInteger(GL11.GL_DEPTH_BITS);
-
         int samples = GL11.glGetInteger(GL13.GL_SAMPLES);
         int sampleBuffers = GL11.glGetInteger(GL13.GL_SAMPLE_BUFFERS);
 

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/LwjglSurfaceFactory.java

 import com.ferox.renderer.*;
 import com.ferox.renderer.impl.*;
 import org.lwjgl.LWJGLException;
+import org.lwjgl.LWJGLUtil;
+import org.lwjgl.opengl.ContextAttribs;
 import org.lwjgl.opengl.Display;
 
 import java.util.HashMap;
     private final DisplayMode defaultMode;
     private final Map<DisplayMode, org.lwjgl.opengl.DisplayMode> convertMap;
 
+    private final ContextAttribs attribs;
+
     /**
      * Create a new LwjglSurfaceFactory.
      */
     public LwjglSurfaceFactory() {
-        boolean forceNoPBuffer = Boolean.getBoolean("ferox.disable.pbuffer");
-        boolean forceNoFBO = Boolean.getBoolean("ferox.disable.fbo");
+        if (LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_MACOSX &&
+            LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 7)) {
+            // for mac we need to explicitly select this context profile to get 3+
+            attribs = new ContextAttribs(3, 2).withProfileCore(true);
+        } else {
+            // for everyone else, it should just automatically select the highest opengl version
+            attribs = null;
+        }
 
         convertMap = new HashMap<>();
-
         try {
             org.lwjgl.opengl.DisplayMode[] modes = Display.getAvailableDisplayModes();
             for (org.lwjgl.opengl.DisplayMode lwjglMode : modes) {
         }
         defaultMode = convert(Display.getDesktopDisplayMode());
 
-        caps = LwjglCapabilities
-                .computeCapabilities(convertMap.keySet().toArray(new DisplayMode[convertMap.size()]),
-                                     forceNoPBuffer, forceNoFBO);
+        caps = LwjglCapabilities.computeCapabilities(attribs, convertMap.keySet().toArray(
+                new DisplayMode[convertMap.size()]));
     }
 
     /**
         return convertMap.get(mode);
     }
 
+    /**
+     * @return Get the selected ContextAttribs that must be specified when creating any context or surface
+     *         used by this factory.
+     */
+    public ContextAttribs getContextAttribs() {
+        return attribs;
+    }
+
     private static DisplayMode convert(org.lwjgl.opengl.DisplayMode lwjglMode) {
         return new DisplayMode(lwjglMode.getWidth(), lwjglMode.getHeight(), lwjglMode.getBitsPerPixel(),
                                lwjglMode.getFrequency());

ferox-renderer-lwjgl/src/main/java/com/ferox/renderer/impl/lwjgl/PbufferShadowContext.java

         Drawable realShare = (shareWith == null ? null : shareWith.getDrawable());
         Pbuffer pbuffer;
         try {
-            pbuffer = new Pbuffer(1, 1, new PixelFormat(), realShare);
+            pbuffer = new Pbuffer(1, 1, new PixelFormat(), null, realShare, creator.getContextAttribs());
         } catch (LWJGLException e) {
             throw new FrameworkException("Unable to create Pbuffer", e);
         }

ferox-renderer-lwjgl/src/test/java/com/ferox/renderer/impl/lwjgl/LwjglFixedFunctionTest.java

 
         final Geometry box = Box.create(framework, 3.0);
 
-        while (!s.isDestroyed()) {
-            framework.invoke(new Task<Void>() {
-                @Override
-                public Void run(HardwareAccessLayer access) {
-                    Context c = access.setActiveSurface(s);
-                    FixedFunctionRenderer r = c.getFixedFunctionRenderer();
-                    r.clear(true, true, true);
+        try {
+            while (!s.isDestroyed()) {
+                framework.invoke(new Task<Void>() {
+                    @Override
+                    public Void run(HardwareAccessLayer access) {
+                        Context c = access.setActiveSurface(s);
+                        FixedFunctionRenderer r = c.getFixedFunctionRenderer();
+                        r.clear(true, true, true);
 
-                    Frustum view = new Frustum(60.0, 1.0, 1.0, 1000.0);
-                    view.setOrientation(new Vector3(0, 0, 150), new Vector3(0, 0, -1), new Vector3(0, 1, 0));
-                    r.setProjectionMatrix(view.getProjectionMatrix());
-                    r.setModelViewMatrix(view.getViewMatrix());
+                        Frustum view = new Frustum(60.0, 1.0, 1.0, 1000.0);
+                        view.setOrientation(new Vector3(0, 0, 150), new Vector3(0, 0, -1),
+                                            new Vector3(0, 1, 0));
+                        r.setProjectionMatrix(view.getProjectionMatrix());
+                        r.setModelViewMatrix(view.getViewMatrix());
 
-                    r.setLightingEnabled(true);
-                    r.setLightEnabled(0, true);
-                    r.setLightColor(0, new Vector4(.4, .2, 0, 1), new Vector4(.4, .2, 0, 1),
-                                    new Vector4(1, 1, 1, 1));
-                    r.setLightPosition(0, new Vector4(50, 50, 50, 1));
+                        r.setLightingEnabled(true);
+                        r.setLightEnabled(0, true);
+                        r.setLightColor(0, new Vector4(.4, .2, 0, 1), new Vector4(.4, .2, 0, 1),
+                                        new Vector4(1, 1, 1, 1));
+                        r.setLightPosition(0, new Vector4(50, 50, 50, 1));
 
-                    r.setMaterialDiffuse(new Vector4(.8, .8, .8, 1));
+                        r.setMaterialDiffuse(new Vector4(.8, .8, .8, 1));
 
-                    r.setNormals(box.getNormals());
-                    r.setVertices(box.getVertices());
-                    r.setIndices(box.getIndices());
+                        r.setNormals(box.getNormals());
+                        r.setVertices(box.getVertices());
+                        r.setIndices(box.getIndices());
 
 
-                    Matrix4 m = new Matrix4();
-                    Vector4 t = new Vector4();
-                    for (int i = 0; i < 10000; i++) {
-                        t.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50,
-                              1);
-                        m.setIdentity().setCol(3, t);
-                        r.setModelViewMatrix(m.mul(view.getViewMatrix(), m));
+                        Matrix4 m = new Matrix4();
+                        Vector4 t = new Vector4();
+                        for (int i = 0; i < 10000; i++) {
+                            t.set(Math.random() * 100 - 50, Math.random() * 100 - 50,
+                                  Math.random() * 100 - 50, 1);
+                            m.setIdentity().setCol(3, t);
+                            r.setModelViewMatrix(m.mul(view.getViewMatrix(), m));
 
-                        r.render(box.getPolygonType(), box.getIndexOffset(), box.getIndexCount());
+                            r.render(box.getPolygonType(), box.getIndexOffset(), box.getIndexCount());
+                        }
+
+                        c.flush();
+
+                        return null;
                     }
-
-                    c.flush();
-
-                    return null;
-                }
-            }).get();
+                }).get();
+            }
+        } finally {
+            framework.destroy();
         }
-        framework.destroy();
     }
 }

ferox-renderer/src/main/java/com/ferox/renderer/HardwareAccessLayer.java

      * <p/>
      * This is the preferred method for refreshing resources when performance is critical.
      * <p/>
-     * Refreshing a resource may affect the state of any renderer that is in use if there's a current context.
-     * This can potentially invalidate any buffer or texture configuration. It is best to only perform
-     * refreshes before or after using a renderer, but never intermingled. If necessary, be sure to update the
-     * renderer after the refresh.
+     * Refreshing a resource may affect the state of any renderer that is in use. This can potentially
+     * invalidate any buffer or texture configuration. It is best to only perform refreshes before or after
+     * using a renderer, but never intermingled.
      *
      * @param resource The resource to update
      *
      * <p/>
      * This is the preferred method for destroying resources when performance and exact control over timing is
      * necessary. Like {@link #refresh(Resource)} it avoids the cost of allocating and queuing a task.
+     * Destroying a resource may affect the state of any renderer that it in use. This can potentially
+     * invalidate any buffer or texture configuration. It is best to only perform destructive actions before
+     * after using the renderr, but never intermingled..
      *
      * @param resource The resource to destroy
      *

ferox-renderer/src/main/java/com/ferox/renderer/Renderer.java

  * <p/>
  * There are two sub-implementations of Renderer that provide access to the old fixed-function pipeline in
  * OpenGL, and a shader based Renderer that is compatible with shaders in the old and new versions of OpenGL.
+ * <p/>
+ * Common behavior shared by all renderers is that exceptions will be thrown when destroyed resources are
+ * provided to them.  However, if state is restored with a call such as {@link
+ * GlslRenderer#setCurrentState(ContextState)}, resources that were destroyed in the meantime will be silently
+ * ignored.
  *
  * @author Michael Ludwig
  * @see FixedFunctionRenderer

ferox-renderer/src/main/java/com/ferox/renderer/impl/AbstractFixedFunctionRenderer.java

             setTextureTransform(i, tf.textureMatrix);
         }
 
+        // set true modelview matrix
+        setModelViewMatrix(f.modelView);
+
+        // note these bypass the destroyed check performed by the public interface
         if (f.vertexBinding.vbo == null) {
             setAttribute(state.vertexBinding, null, 0, 0, 0);
         } else {
             }
         }
 
-        // set true modelview matrix
-        setModelViewMatrix(f.modelView);
-
         // set shared state
         delegate.setCurrentState(shared);
 
         if ((angle < 0.0 || angle > 90.0) && angle != 180.0) {
             throw new IllegalArgumentException("Spotlight angle must be in [0, 90] or be 180, not: " + angle);
         }
-        if (exponent < 0.0 || angle > 128.0) {
+        if (exponent < 0.0 || exponent > 128.0) {
             throw new IllegalArgumentException("Spotlight exponent must be in [0, 128], not: " + exponent);
         }
 
             enableTexture(tex, null);
             context.bindTexture(tex, null);
         } else {
+            if (image.isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
+
             TextureImpl.TextureHandle newImage = ((TextureImpl) image).getHandle();
             enableTexture(tex, newImage);
             context.bindTexture(tex, newImage);
         if (vertices == null) {
             setAttribute(state.vertexBinding, null, 0, 0, 0);
         } else {
+            if (vertices.getVBO().isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
             if (vertices.getElementSize() == 1) {
                 throw new IllegalArgumentException("Vertices element size cannot be 1");
             }
         if (normals == null) {
             setAttribute(state.normalBinding, null, 0, 0, 0);
         } else {
+            if (normals.getVBO().isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
             if (normals.getElementSize() != 3) {
                 throw new IllegalArgumentException("Normals element size must be 3");
             }
             glMaterialColor(ColorPurpose.DIFFUSE, DEFAULT_MAT_D_COLOR);
             state.matDiffuse.set(DEFAULT_MAT_D_COLOR);
         } else {
+            if (colors.getVBO().isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
             if (colors.getElementSize() != 3 && colors.getElementSize() != 4) {
                 throw new IllegalArgumentException("Colors element size must be 3 or 4");
             }
         if (texCoords == null) {
             setAttribute(state.texCoordBindings[tex], null, 0, 0, 0);
         } else {
+            if (texCoords.getVBO().isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
             if (texCoords.getVBO().getDataType().isNormalized()) {
                 throw new IllegalArgumentException("Texture coordinates do not accept normalized types");
             }

ferox-renderer/src/main/java/com/ferox/renderer/impl/AbstractGlslRenderer.java

         delegate.setCurrentState(sharedState);
 
         // set all attributes
+        // note that the attribute and uniform sampler configuration bypasses the destruction checks
+        // performed by the public interface
         for (int i = 0; i < shaderState.attributes.length; i++) {
             ShaderOnlyState.AttributeState a = shaderState.attributes[i];
             if (a.vbo != null && !a.vbo.isDestroyed()) {
         if (shader == null) {
             context.bindShader(null);
         } else {
-            context.bindShader(((ShaderImpl) shader).getHandle());
+            if (shader.isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
 
-            // perform maintenance on bound texture state to make it line up with
-            // what the texture uniforms are expecting
-            int unit = 0;
-            Set<Integer> reservedUnits = new HashSet<>();
-            Set<ShaderImpl.UniformImpl> needsInit = new HashSet<>();
+            ShaderImpl.ShaderHandle handle = ((ShaderImpl) shader).getHandle();
+            if (handle != delegate.state.shader) {
+                context.bindShader(handle);
 
-            for (Shader.Uniform u : shader.getUniforms()) {
-                if (u.getType().getPrimitiveType() == null) {
-                    // sampler types have a null primitive type
-                    ShaderImpl.UniformImpl ui = (ShaderImpl.UniformImpl) u;
-                    if (ui.initialized) {
-                        // mark unit as reserved and bind the last assigned sampler
-                        reservedUnits.add(ui.intValues.get(0));
-                        context.bindTexture(ui.intValues.get(0), ui.texture);
+                // perform maintenance on bound texture state to make it line up with
+                // what the texture uniforms are expecting
+                int unit = 0;
+                Set<Integer> reservedUnits = new HashSet<>();
+                Set<ShaderImpl.UniformImpl> needsInit = new HashSet<>();
 
-                        // special clean up if the texture was deleted
-                        if (delegate.state.textures[ui.intValues.get(0)] == null) {
-                            ui.texture = null;
+                for (Shader.Uniform u : shader.getUniforms()) {
+                    if (u.getType().getPrimitiveType() == null) {
+                        // sampler types have a null primitive type
+                        ShaderImpl.UniformImpl ui = (ShaderImpl.UniformImpl) u;
+                        if (ui.initialized) {
+                            // mark unit as reserved and bind the last assigned sampler
+                            reservedUnits.add(ui.intValues.get(0));
+                            context.bindTexture(ui.intValues.get(0), ui.texture);
+
+                            // special clean up if the texture was deleted
+                            if (delegate.state.textures[ui.intValues.get(0)] == null) {
+                                ui.texture = null;
+                            }
+                        } else {
+                            needsInit.add(ui);
                         }
-                    } else {
-                        needsInit.add(ui);
                     }
                 }
-            }
 
-            // complete initialization now that reservedUnits holds all used texture units
-            for (ShaderImpl.UniformImpl u : needsInit) {
-                // search for an unreserved unit
-                while (reservedUnits.contains(unit)) {
-                    unit++;
+                // complete initialization now that reservedUnits holds all used texture units
+                for (ShaderImpl.UniformImpl u : needsInit) {
+                    // search for an unreserved unit
+                    while (reservedUnits.contains(unit)) {
+                        unit++;
+                    }
+
+                    // mark it as reserved and configure the uniform
+                    reservedUnits.add(unit);
+                    u.intValues.put(0, unit);
+                    u.initialized = true;
+                    glUniform(u, u.intValues);
+
+                    // bind a null texture
+                    context.bindTexture(unit, null);
                 }
-
-                // mark it as reserved and configure the uniform
-                reservedUnits.add(unit);
-                u.intValues.put(0, unit);
-                u.initialized = true;
-                glUniform(u, u.intValues);
-
-                // bind a null texture
-                context.bindTexture(unit, null);
             }
         }
     }
 
         ShaderOnlyState.AttributeState a = state.attributes[attribute.getIndex() + column];
         if (attr != null) {
+            if (attr.getVBO().isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
+
             // validate buffer type consistent with attribute type
             switch (attribute.getType().getPrimitiveType()) {
             case FLOAT:
 
         int textureUnit = u.intValues.get(0);
         if (texture != null) {
+            if (texture.isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
+
             TextureImpl.TextureHandle handle = ((TextureImpl) texture).getHandle();
             validateSamplerType(var.getType(), texture);
 

ferox-renderer/src/main/java/com/ferox/renderer/impl/AbstractRenderer.java

  *
  * @author Michael Ludwig
  */
-public abstract class AbstractRenderer implements Renderer {
+public abstract class AbstractRenderer implements Renderer, Activateable {
     private static final Vector4 BLACK = new Vector4(0, 0, 0, 0);
 
     protected final RendererDelegate delegate;
         return delegate.render(polyType, first, count);
     }
 
-    /**
-     * Notify the renderer that the provided surface has been activated and will be using this Renderer.
-     *
-     * @param active The now active surface
-     */
+    @Override
     public void activate(AbstractSurface active) {
         delegate.activate(active);
     }

ferox-renderer/src/main/java/com/ferox/renderer/impl/AbstractSurface.java

 package com.ferox.renderer.impl;
 
 import com.ferox.renderer.Context;
-import com.ferox.renderer.FixedFunctionRenderer;
-import com.ferox.renderer.GlslRenderer;
 import com.ferox.renderer.Surface;
 
 import java.util.concurrent.Callable;
      * onSurfaceActivate() is a listener method that is invoked by ContextManager when a surface is activated.
      * The provided context is the current context on the calling thread and will not be null.
      * <p/>
-     * This method can be overridden by subclasses to perform more actions. The current implementation
-     * activates any renderers the context provides.
+     * This method can be overridden by subclasses to perform more actions.
      *
      * @param context The current context
      */
     public void onSurfaceActivate(OpenGLContext context) {
-        FixedFunctionRenderer ffp = context.getFixedFunctionRenderer();
-        if (ffp instanceof AbstractRenderer) {
-            ((AbstractRenderer) ffp).activate(this);
-        }
-
-        GlslRenderer glsl = context.getGlslRenderer();
-        if (glsl instanceof AbstractRenderer) {
-            ((AbstractRenderer) glsl).activate(this);
-        }
     }
 
     /**
      * onSurfaceDeactivate() is a listener method that is invoked by ContextManager when a surface is
      * deactivated. The provided context is the current context on the calling thread and will not be null.
-     * This method can be overridden by subclasses to perform more actions. The current implementation resets
-     * any renderers the context has.
+     * This method can be overridden by subclasses to perform more actions.
      *
      * @param context The current context
      */
     public void onSurfaceDeactivate(OpenGLContext context) {
-        // Reset the renderers so that the next task sees a clean slate
-        FixedFunctionRenderer ffp = context.getFixedFunctionRenderer();
-        if (ffp != null) {
-            ffp.reset();
-        }
-
-        GlslRenderer glsl = context.getGlslRenderer();
-        if (glsl != null) {
-            glsl.reset();
-        }
     }
 
     @Override

ferox-renderer/src/main/java/com/ferox/renderer/impl/Activateable.java

+package com.ferox.renderer.impl;
+
+/**
+ *
+ */
+public interface Activateable {
+    /**
+     * Notify the renderer that the provided surface has been activated and will be using this Renderer.
+     *
+     * @param active The now active surface
+     */
+    public void activate(AbstractSurface active);
+}

ferox-renderer/src/main/java/com/ferox/renderer/impl/ContextManager.java

         public ContextThread(ThreadGroup group, String name) {
             super(group, name);
             tasks = new LinkedBlockingDeque<>(10);
-            setDaemon(true);
         }
 
         public OpenGLContext ensureContext() {

ferox-renderer/src/main/java/com/ferox/renderer/impl/DestructibleManager.java

 
 import com.ferox.renderer.Destructible;
 
+import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
 import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
     }
 
     private final ReferenceQueue<Destructible> monitors;
-    private final ConcurrentHashMap<ManagedDestructible, Boolean> managedInstances;
+    private final ConcurrentHashMap<ManagedDestructible, DestructibleReference> managedInstances;
 
     private LifeCycleManager lifecycle;
 
         // start a low priority managed thread
         Thread destroyer = new Thread(lifecycle.getManagedThreadGroup(), new WeakReferenceMonitor(),
                                       "destructible-gc-thread");
-        destroyer.setDaemon(true);
         lifecycle.startManagedThread(destroyer, false);
     }
 
         lifecycle.getLock().lock();
         try {
             if (!lifecycle.isStopped()) {
-                managedInstances.put(realInstance, true);
-                new DestructibleReference(exposed, realInstance, monitors);
+                managedInstances
+                        .put(realInstance, new DestructibleReference(exposed, realInstance, monitors));
             }
         } finally {
             lifecycle.getLock().unlock();
         }
     }
 
-    private class DestructibleReference extends WeakReference<Destructible> {
+    private class DestructibleReference extends PhantomReference<Destructible> {
         final ManagedDestructible destructible;
 
         public DestructibleReference(Destructible exposedInstance, ManagedDestructible realInstance,

ferox-renderer/src/main/java/com/ferox/renderer/impl/HardwareAccessLayerImpl.java

             if (selectedRenderer == null) {
                 // need to select a renderer
                 selectedRenderer = context.getGlslRenderer();
+                if (selectedRenderer instanceof Activateable) {
+                    ((Activateable) selectedRenderer).activate(surface);
+                }
+                selectedRenderer.reset();
             }
 
             if (selectedRenderer instanceof FixedFunctionRenderer) {
             if (selectedRenderer == null) {
                 // need to select a renderer
                 selectedRenderer = context.getFixedFunctionRenderer();
+                if (selectedRenderer instanceof Activateable) {
+                    ((Activateable) selectedRenderer).activate(surface);
+                }
+                selectedRenderer.reset();
             }
 
             if (selectedRenderer instanceof GlslRenderer) {

ferox-renderer/src/main/java/com/ferox/renderer/impl/RendererDelegate.java

 import com.ferox.renderer.ElementBuffer;
 import com.ferox.renderer.Renderer;
 import com.ferox.renderer.Renderer.*;
+import com.ferox.renderer.ResourceException;
 import com.ferox.renderer.impl.resources.BufferImpl;
 
 /**
         setStencilTestEnabled(state.stencilEnabled);
 
         setViewport(state.viewX, state.viewY, state.viewWidth, state.viewHeight);
+
+        // note that these bypass the destroyed check that throws an exception from the public interface
         setIndicesHandle(state.elementVBO);
-
         context.bindShader(state.shader);
         for (int i = 0; i < state.textures.length; i++) {
             context.bindTexture(i, state.textures[i]);
         }
 
-        // clean out resource bindings that marked destroyed
+        // so we have to clean out resource bindings that marked destroyed
         if (state.shader != null && state.shader.isDestroyed()) {
             context.bindShader(null);
         }
         if (indices == null) {
             setIndicesHandle(null);
         } else {
+            if (indices.isDestroyed()) {
+                throw new ResourceException("Cannot use a destroyed resource");
+            }
             setIndicesHandle(((BufferImpl) indices).getHandle());
         }
     }

ferox-renderer/src/main/java/com/ferox/renderer/impl/ShaderBackedFixedFunctionRenderer.java

-package com.ferox.renderer.impl;
-
-import com.ferox.math.Const;
-import com.ferox.math.Matrix4;
-import com.ferox.math.Vector3;
-import com.ferox.math.Vector4;
-import com.ferox.renderer.*;
-
-/**
- *
- */
-public class ShaderBackedFixedFunctionRenderer implements FixedFunctionRenderer {
-    private final GlslRenderer glsl;
-
-    private FixedFunctionState.MatrixMode matrixMode;
-    private final Vector3 cachedFogConfig = new Vector3();
-    private final Vector3 cachedAttenuations = new Vector3();
-
-    // lazily allocated during the first activate()
-    private ContextState<GlslRenderer> defaultState;
-
-    /*
-     * Vertex shader uniforms
-     */
-    private Shader.Uniform modelviewMatrix; // mat4
-    private Shader.Uniform projectionMatrix; // mat4
-
-    private Shader.Uniform enableLighting; // bool
-
-    private Shader.Uniform globalAmbient; // vec4
-    private Shader.Uniform[] enableSingleLight; // bool[8]
-    private Shader.Uniform[] lightPosition; // vec4[8]
-    private Shader.Uniform[] ambientLightColors; // vec4[8]
-    private Shader.Uniform[] diffuseLightColors; // vec4[8]
-    private Shader.Uniform[] specularLightColors; // vec4[8]
-    private Shader.Uniform[] spotlightDirections; // vec3[8]
-    private Shader.Uniform[] spotlightCutoffs; // float[8]
-    private Shader.Uniform[] spotlightExponents; // float[8]
-    private Shader.Uniform[] lightAttenuations; // vec3[8]
-
-    private Shader.Uniform ambientMaterial; // vec4
-    private Shader.Uniform specularMaterial; // vec4
-    private Shader.Uniform emittedMaterial; // vec4
-    private Shader.Uniform shininess; // float
-
-    /*
-     * Fragment shader uniforms
-     */
-    private Shader.Uniform enableAlphaTest; // bool
-    private Shader.Uniform alphaTest; // int
-    private Shader.Uniform alphaRefValue; // float
-
-    private Shader.Uniform fogConfig; // vec3 0 = start/density 1 = end 2 = signal (0 = linear, > = exp, < = exp squared)
-    private Shader.Uniform fogColor; // vec4
-    private Shader.Uniform enableFog; // bool
-
-    /*
-     * Shader attributes
-     */
-    private Shader.Attribute vertices;
-    private Shader.Attribute normals;
-    private Shader.Attribute colors;
-
-    public ShaderBackedFixedFunctionRenderer(GlslRenderer shaderRenderer) {
-        glsl = shaderRenderer;
-        defaultState = null;
-    }
-
-    @Override
-    public void setStencilUpdate(StencilUpdate stencilFail, StencilUpdate depthFail,
-                                 StencilUpdate depthPass) {
-        initializeMaybe();
-        glsl.setStencilUpdate(stencilFail, depthFail, depthPass);
-    }
-
-    @Override
-    public void setStencilUpdateFront(StencilUpdate stencilFail, StencilUpdate depthFail,
-                                      StencilUpdate depthPass) {
-        initializeMaybe();
-        glsl.setStencilUpdateFront(stencilFail, depthFail, depthPass);
-    }
-
-    @Override
-    public void setStencilUpdateBack(StencilUpdate stencilFail, StencilUpdate depthFail,
-                                     StencilUpdate depthPass) {
-        initializeMaybe();
-        glsl.setStencilUpdateBack(stencilFail, depthFail, depthPass);
-    }
-
-    @Override
-    public void setStencilTest(Comparison test, int refValue, int testMask) {
-        initializeMaybe();
-        glsl.setStencilTest(test, refValue, testMask);
-    }
-
-    @Override
-    public void setStencilTestFront(Comparison test, int refValue, int testMask) {
-        initializeMaybe();
-        glsl.setStencilTestFront(test, refValue, testMask);
-    }
-
-    @Override
-    public void setStencilTestBack(Comparison test, int refValue, int testMask) {
-        initializeMaybe();
-        glsl.setStencilTestBack(test, refValue, testMask);
-    }
-
-    @Override
-    public void setStencilTestEnabled(boolean enable) {
-        initializeMaybe();
-        glsl.setStencilTestEnabled(enable);
-    }
-
-    @Override
-    public void setStencilWriteMask(int mask) {
-        initializeMaybe();
-        glsl.setStencilWriteMask(mask);
-    }
-
-    @Override
-    public void setStencilWriteMask(int front, int back) {
-        initializeMaybe();
-        glsl.setStencilWriteMask(front, back);
-    }
-
-    @Override
-    public void setDepthTest(Comparison test) {
-        initializeMaybe();
-        glsl.setDepthTest(test);
-    }
-
-    @Override
-    public void setDepthWriteMask(boolean mask) {
-        initializeMaybe();
-        glsl.setDepthWriteMask(mask);
-    }
-
-    @Override
-    public void setDepthOffsets(double factor, double units) {
-        initializeMaybe();
-        glsl.setDepthOffsets(factor, units);
-    }
-
-    @Override
-    public void setDepthOffsetsEnabled(boolean enable) {
-        initializeMaybe();
-        glsl.setDepthOffsetsEnabled(enable);
-    }
-
-    @Override
-    public void setDrawStyle(DrawStyle style) {
-        initializeMaybe();
-        glsl.setDrawStyle(style);
-    }
-
-    @Override
-    public void setDrawStyle(DrawStyle front, DrawStyle back) {
-        initializeMaybe();
-        glsl.setDrawStyle(front, back);
-    }
-
-    @Override
-    public void setColorWriteMask(boolean red, boolean green, boolean blue, boolean alpha) {
-        initializeMaybe();
-        glsl.setColorWriteMask(red, green, blue, alpha);
-    }
-
-    @Override
-    public void setBlendingEnabled(boolean enable) {
-        initializeMaybe();
-        glsl.setBlendingEnabled(enable);
-    }
-
-    @Override
-    public void setBlendColor(@Const Vector4 color) {
-        initializeMaybe();
-        glsl.setBlendColor(color);
-    }
-
-    @Override
-    public void setBlendMode(BlendFunction function, BlendFactor src, BlendFactor dst) {
-        initializeMaybe();
-        glsl.setBlendMode(function, src, dst);
-    }
-
-    @Override
-    public void setBlendModeRgb(BlendFunction function, BlendFactor src, BlendFactor dst) {
-        initializeMaybe();
-        glsl.setBlendModeRgb(function, src, dst);
-    }
-
-    @Override
-    public void setBlendModeAlpha(BlendFunction function, BlendFactor src, BlendFactor dst) {
-        initializeMaybe();
-        glsl.setBlendModeAlpha(function, src, dst);
-    }
-
-    @Override
-    public void setIndices(ElementBuffer indices) {
-        initializeMaybe();
-        glsl.setIndices(indices);
-    }
-
-    @Override
-    public int render(PolygonType polyType, int offset, int count) {
-        initializeMaybe();
-        return glsl.render(polyType, offset, count);
-    }
-
-    @Override
-    public void clear(boolean clearColor, boolean clearDepth, boolean clearStencil) {
-        initializeMaybe();
-        glsl.clear(clearColor, clearDepth, clearStencil);
-    }
-
-    @Override
-    public void clear(boolean clearColor, boolean clearDepth, boolean clearStencil, @Const Vector4 color,
-                      double depth, int stencil) {
-        initializeMaybe();
-        glsl.clear(clearColor, clearDepth, clearStencil, color, depth, stencil);
-    }
-
-    @Override
-    public void setViewport(int x, int y, int width, int height) {
-        initializeMaybe();
-        glsl.setViewport(x, y, width, height);
-    }
-
-    @Override
-    public ContextState<FixedFunctionRenderer> getCurrentState() {
-        return new WrappedState(glsl.getCurrentState());
-    }
-
-    @Override
-    public void reset() {
-        glsl.setCurrentState(defaultState);
-    }
-
-    @Override
-    public void setCurrentState(ContextState<FixedFunctionRenderer> state) {
-        WrappedState s = (WrappedState) state;
-        glsl.setCurrentState(s.realState);
-    }
-
-    @Override
-    public void setFogEnabled(boolean enable) {
-        initializeMaybe();
-        glsl.setUniform(enableFog, enable);
-    }
-
-    @Override
-    public void setFogColor(@Const Vector4 color) {
-        initializeMaybe();
-        glsl.setUniform(fogColor, color);
-    }
-
-    @Override
-    public void setFogLinear(double start, double end) {
-        initializeMaybe();
-        cachedFogConfig.set(start, end, 0.0);
-        glsl.setUniform(fogConfig, cachedFogConfig);
-    }
-
-    @Override
-    public void setFogExponential(double density, boolean squared) {
-        initializeMaybe();
-        cachedFogConfig.set(density, 0.0, squared ? -1.0 : 1.0);
-        glsl.setUniform(fogConfig, cachedFogConfig);
-    }
-
-    @Override
-    public void setPointAntiAliasingEnabled(boolean enable) {
-    }
-
-    @Override
-    public void setLineAntiAliasingEnabled(boolean enable) {
-    }
-
-    @Override
-    public void setPolygonAntiAliasingEnabled(boolean enable) {
-    }
-
-    @Override
-    public void setPointSize(double width) {
-    }
-
-    @Override
-    public void setLineSize(double width) {
-    }
-
-    @Override
-    public void setAlphaTest(Comparison test, double refValue) {
-        initializeMaybe();
-        glsl.setUniform(enableAlphaTest, test != Comparison.ALWAYS);
-        glsl.setUniform(alphaTest, test.ordinal());
-        glsl.setUniform(alphaRefValue, refValue);
-    }
-
-    @Override
-    public void setLightingEnabled(boolean enable) {
-        initializeMaybe();
-        glsl.setUniform(enableLighting, enable);
-    }
-
-    @Override
-    public void setGlobalAmbientLight(@Const Vector4 ambient) {
-        initializeMaybe();
-        glsl.setUniform(globalAmbient, ambient);
-    }
-
-    @Override
-    public void setLightEnabled(int light, boolean enable) {
-        initializeMaybe();
-        glsl.setUniform(enableSingleLight[light], enable);
-    }
-
-    @Override
-    public void setLightPosition(int light, @Const Vector4 pos) {
-        initializeMaybe();
-        // FIXME validate w
-        // FIXME transform into eye space
-        glsl.setUniform(lightPosition[light], pos);
-    }
-
-    @Override
-    public void setLightColor(int light, @Const Vector4 amb, @Const Vector4 diff, @Const Vector4 spec) {
-        initializeMaybe();
-        glsl.setUniform(ambientLightColors[light], amb);
-        glsl.setUniform(diffuseLightColors[light], diff);
-        glsl.setUniform(specularLightColors[light], spec);
-    }
-
-    @Override
-    public void setSpotlight(int light, @Const Vector3 dir, double angle, double exponent) {
-        initializeMaybe();
-        // FIXME prenormalize the direction
-        // FIXME transform into eye space
-        glsl.setUniform(spotlightDirections[light], dir);
-        glsl.setUniform(spotlightCutoffs[light], angle);
-        glsl.setUniform(spotlightExponents[light], exponent);
-    }
-
-    @Override
-    public void setLightAttenuation(int light, double constant, double linear, double quadratic) {
-        initializeMaybe();
-        cachedAttenuations.set(constant, linear, quadratic);
-        glsl.setUniform(lightAttenuations[light], cachedAttenuations);
-    }
-
-    @Override
-    public void setMaterial(@Const Vector4 amb, @Const Vector4 diff, @Const Vector4 spec,
-                            @Const Vector4 emm) {
-        setMaterialAmbient(amb);
-        setMaterialDiffuse(diff);
-        setMaterialSpecular(spec);
-        setMaterialEmissive(emm);
-    }
-
-    @Override
-    public void setMaterialDiffuse(@Const Vector4 diff) {
-        initializeMaybe();
-        glsl.bindAttribute(colors, diff);
-    }
-
-    @Override
-    public void setMaterialAmbient(@Const Vector4 amb) {
-        initializeMaybe();
-        glsl.setUniform(ambientMaterial, amb);
-    }
-
-    @Override
-    public void setMaterialSpecular(@Const Vector4 spec) {
-        initializeMaybe();
-        glsl.setUniform(specularMaterial, spec);
-    }
-
-    @Override
-    public void setMaterialEmissive(@Const Vector4 emm) {
-        initializeMaybe();
-        glsl.setUniform(emittedMaterial, emm);
-    }
-
-    @Override
-    public void setMaterialShininess(double shininess) {
-        initializeMaybe();
-        glsl.setUniform(this.shininess, shininess);
-    }
-
-    @Override
-    public void setTexture(int tex, Sampler image) {
-    }
-
-    @Override
-    public void setTextureColor(int tex, @Const Vector4 color) {
-    }
-
-    @Override
-    public void setTextureCoordGeneration(int tex, TexCoordSource gen) {
-    }
-
-    @Override
-    public void setTextureCoordGeneration(int tex, TexCoord coord, TexCoordSource gen) {
-    }
-
-    @Override
-    public void setTextureObjectPlane(int tex, TexCoord coord, @Const Vector4 plane) {
-    }
-
-    @Override
-    public void setTextureObjectPlanes(int tex, @Const Matrix4 planes) {
-    }
-
-    @Override
-    public void setTextureEyePlane(int tex, TexCoord coord, @Const Vector4 plane) {
-    }
-
-    @Override
-    public void setTextureEyePlanes(int tex, @Const Matrix4 planes) {
-    }
-
-    @Override
-    public void setTextureTransform(int tex, @Const Matrix4 matrix) {
-    }
-
-    @Override
-    public void setTextureCombineRGB(int tex, CombineFunction function, CombineSource src0,
-                                     CombineOperand op0, CombineSource src1, CombineOperand op1,
-                                     CombineSource src2, CombineOperand op2) {
-    }
-
-    @Override
-    public void setTextureCombineAlpha(int tex, CombineFunction function, CombineSource src0,
-                                       CombineOperand op0, CombineSource src1, CombineOperand op1,
-                                       CombineSource src2, CombineOperand op2) {
-    }
-
-    @Override
-    public void setProjectionMatrix(@Const Matrix4 projection) {
-        initializeMaybe();
-        glsl.setUniform(projectionMatrix, projection);
-    }
-
-    @Override
-    public void setModelViewMatrix(@Const Matrix4 modelView) {
-        initializeMaybe();
-        glsl.setUniform(modelviewMatrix, modelView);
-        // FIXME store modelview so that we can compute eye space params for lights and eye planes
-    }
-
-    @Override
-    public void setVertices(VertexAttribute vertices) {
-        initializeMaybe();
-        glsl.bindAttribute(this.vertices, vertices);
-    }
-
-    @Override
-    public void setNormals(VertexAttribute normals) {
-        initializeMaybe();
-        glsl.bindAttribute(this.normals, normals);
-    }
-
-    @Override
-    public void setColors(VertexAttribute colors) {
-        initializeMaybe();
-        glsl.bindAttribute(this.colors, colors);
-    }
-
-    @Override
-    public void setTextureCoordinates(int tex, VertexAttribute texCoords) {
-    }
-
-    private void initializeMaybe() {
-
-    }
-
-    private static class WrappedState implements ContextState<FixedFunctionRenderer> {
-        private final ContextState<GlslRenderer> realState;
-
-        public WrappedState(ContextState<GlslRenderer> realState) {
-            this.realState = realState;
-        }
-    }
-}

ferox-renderer/src/main/java/com/ferox/renderer/impl/ShaderFixedFunctionEmulator.java

+package com.ferox.renderer.impl;
+
+import com.ferox.math.Const;
+import com.ferox.math.Matrix4;
+import com.ferox.math.Vector3;
+import com.ferox.math.Vector4;
+import com.ferox.renderer.*;
+import com.ferox.renderer.builder.ShaderBuilder;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ *
+ */
+public class ShaderFixedFunctionEmulator implements FixedFunctionRenderer, Activateable {
+    private static final String VERTEX_SHADER = "ffp.vert";
+    private static final String FRAGMENT_SHADER = "ffp.frag";
+
+    private final GlslRenderer glsl;
+
+    private final Vector3 cachedFogConfig = new Vector3();
+    private final Vector3 cachedAttenuations = new Vector3();
+
+    // lazily allocated during the first activate()
+    private Shader shader;
+    private ContextState<GlslRenderer> defaultState;
+
+    /*
+     * Vertex shader uniforms
+     */
+    private Shader.Uniform modelviewMatrix; // mat4
+    private Shader.Uniform projectionMatrix; // mat4
+
+    private Shader.Uniform enableLighting; // bool
+
+    private Shader.Uniform globalAmbient; // vec4
+    private Shader.Uniform[] enableSingleLight; // bool[8]
+    private Shader.Uniform[] lightPosition; // vec4[8]
+    private Shader.Uniform[] ambientLightColors; // vec4[8]
+    private Shader.Uniform[] diffuseLightColors; // vec4[8]
+    private Shader.Uniform[] specularLightColors; // vec4[8]
+    private Shader.Uniform[] spotlightDirections; // vec3[8]
+    private Shader.Uniform[] spotlightCutoffs; // float[8]
+    private Shader.Uniform[] spotlightExponents; // float[8]
+    private Shader.Uniform[] lightAttenuations; // vec3[8]
+
+    private Shader.Uniform ambientMaterial; // vec4
+    private Shader.Uniform specularMaterial; // vec4
+    private Shader.Uniform emittedMaterial; // vec4
+    private Shader.Uniform shininess; // float
+
+    /*
+     * Fragment shader uniforms
+     */
+    private Shader.Uniform enableAlphaTest; // bool
+    private Shader.Uniform alphaTest; // int
+    private Shader.Uniform alphaRefValue; // float
+
+    private Shader.Uniform fogConfig; // vec3 0 = start/density 1 = end 2 = signal (0 = linear, > = exp, < = exp squared)
+    private Shader.Uniform fogColor; // vec4
+    private Shader.Uniform enableFog; // bool
+
+    /*
+     * Shader attributes
+     */
+    private Shader.Attribute vertices;
+    private Shader.Attribute normals;
+    private Shader.Attribute colors;
+
+    public ShaderFixedFunctionEmulator(GlslRenderer shaderRenderer) {
+        glsl = shaderRenderer;
+        shader = null;
+    }
+
+    @Override
+    public void setStencilUpdate(StencilUpdate stencilFail, StencilUpdate depthFail,
+                                 StencilUpdate depthPass) {
+        glsl.setStencilUpdate(stencilFail, depthFail, depthPass);
+    }
+
+    @Override
+    public void setStencilUpdateFront(StencilUpdate stencilFail, StencilUpdate depthFail,
+                                      StencilUpdate depthPass) {
+        glsl.setStencilUpdateFront(stencilFail, depthFail, depthPass);
+    }
+
+    @Override
+    public void setStencilUpdateBack(StencilUpdate stencilFail, StencilUpdate depthFail,
+                                     StencilUpdate depthPass) {
+        glsl.setStencilUpdateBack(stencilFail, depthFail, depthPass);
+    }
+
+    @Override
+    public void setStencilTest(Comparison test, int refValue, int testMask) {
+        glsl.setStencilTest(test, refValue, testMask);
+    }
+
+    @Override
+    public void setStencilTestFront(Comparison test, int refValue, int testMask) {
+        glsl.setStencilTestFront(test, refValue, testMask);
+    }
+
+    @Override
+    public void setStencilTestBack(Comparison test, int refValue, int testMask) {
+        glsl.setStencilTestBack(test, refValue, testMask);
+    }
+
+    @Override
+    public void setStencilTestEnabled(boolean enable) {
+        glsl.setStencilTestEnabled(enable);
+    }
+
+    @Override
+    public void setStencilWriteMask(int mask) {
+        glsl.setStencilWriteMask(mask);
+    }
+
+    @Override
+    public void setStencilWriteMask(int front, int back) {
+        glsl.setStencilWriteMask(front, back);
+    }
+
+    @Override
+    public void setDepthTest(Comparison test) {
+        glsl.setDepthTest(test);
+    }
+
+    @Override
+    public void setDepthWriteMask(boolean mask) {
+        glsl.setDepthWriteMask(mask);
+    }
+
+    @Override
+    public void setDepthOffsets(double factor, double units) {
+        glsl.setDepthOffsets(factor, units);
+    }
+
+    @Override
+    public void setDepthOffsetsEnabled(boolean enable) {
+        glsl.setDepthOffsetsEnabled(enable);
+    }
+
+    @Override
+    public void setDrawStyle(DrawStyle style) {
+        glsl.setDrawStyle(style);
+    }
+
+    @Override
+    public void setDrawStyle(DrawStyle front, DrawStyle back) {
+        glsl.setDrawStyle(front, back);
+    }
+
+    @Override
+    public void setColorWriteMask(boolean red, boolean green, boolean blue, boolean alpha) {
+        glsl.setColorWriteMask(red, green, blue, alpha);
+    }
+
+    @Override
+    public void setBlendingEnabled(boolean enable) {
+        glsl.setBlendingEnabled(enable);
+    }
+
+    @Override
+    public void setBlendColor(@Const Vector4 color) {
+        glsl.setBlendColor(color);
+    }
+
+    @Override
+    public void setBlendMode(BlendFunction function, BlendFactor src, BlendFactor dst) {
+        glsl.setBlendMode(function, src, dst);
+    }
+
+    @Override
+    public void setBlendModeRgb(BlendFunction function, BlendFactor src, BlendFactor dst) {
+        glsl.setBlendModeRgb(function, src, dst);
+    }
+
+    @Override
+    public void setBlendModeAlpha(BlendFunction function, BlendFactor src, BlendFactor dst) {
+        glsl.setBlendModeAlpha(function, src, dst);
+    }
+
+    @Override
+    public void setIndices(ElementBuffer indices) {
+        glsl.setIndices(indices);
+    }
+
+    @Override
+    public int render(PolygonType polyType, int offset, int count) {
+        return glsl.render(polyType, offset, count);
+    }
+
+    @Override
+    public void clear(boolean clearColor, boolean clearDepth, boolean clearStencil) {
+        glsl.clear(clearColor, clearDepth, clearStencil);
+    }
+