changeset 4274:393e1c8c6e4b

RT-26612: Add anti-aliasing support for ES2 pipeline. Approved by Chien.
author Thor johannesson <thor.johannesson@oracle.com>
date Thu, 11 Jul 2013 15:02:05 -0700
parents 127ae0e98230
children f115a2c302a3
files modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGSubScene.java modules/graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java modules/graphics/src/main/java/com/sun/javafx/tk/TKStage.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedStage.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedState.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/PopupScene.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/PopupStage.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/SceneState.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewScene.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java modules/graphics/src/main/java/com/sun/prism/Graphics.java modules/graphics/src/main/java/com/sun/prism/GraphicsPipeline.java modules/graphics/src/main/java/com/sun/prism/PresentableState.java modules/graphics/src/main/java/com/sun/prism/RenderTarget.java modules/graphics/src/main/java/com/sun/prism/ResourceFactory.java modules/graphics/src/main/java/com/sun/prism/d3d/D3DContext.java modules/graphics/src/main/java/com/sun/prism/d3d/D3DPipeline.java modules/graphics/src/main/java/com/sun/prism/d3d/D3DRTTexture.java modules/graphics/src/main/java/com/sun/prism/d3d/D3DResourceFactory.java modules/graphics/src/main/java/com/sun/prism/d3d/D3DSwapChain.java modules/graphics/src/main/java/com/sun/prism/es2/ES2Context.java modules/graphics/src/main/java/com/sun/prism/es2/ES2Pipeline.java modules/graphics/src/main/java/com/sun/prism/es2/ES2RTTexture.java modules/graphics/src/main/java/com/sun/prism/es2/ES2RTTextureData.java modules/graphics/src/main/java/com/sun/prism/es2/ES2ResourceFactory.java modules/graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java modules/graphics/src/main/java/com/sun/prism/es2/GLContext.java modules/graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderContext.java modules/graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderGraphics.java modules/graphics/src/main/java/com/sun/prism/j2d/J2DPresentable.java modules/graphics/src/main/java/com/sun/prism/j2d/J2DPrismGraphics.java modules/graphics/src/main/java/com/sun/prism/j2d/J2DRTTexture.java modules/graphics/src/main/java/com/sun/prism/j2d/J2DResourceFactory.java modules/graphics/src/main/java/com/sun/prism/null3d/DummyContext.java modules/graphics/src/main/java/com/sun/prism/null3d/DummyRTTexture.java modules/graphics/src/main/java/com/sun/prism/null3d/DummyResourceFactory.java modules/graphics/src/main/java/com/sun/prism/null3d/DummySwapChain.java modules/graphics/src/main/java/com/sun/prism/null3d/NULL3DPipeline.java modules/graphics/src/main/java/com/sun/prism/sw/SWGraphics.java modules/graphics/src/main/java/com/sun/prism/sw/SWPresentable.java modules/graphics/src/main/java/com/sun/prism/sw/SWRTTexture.java modules/graphics/src/main/java/com/sun/prism/sw/SWResourceFactory.java modules/graphics/src/main/java/javafx/scene/Node.java modules/graphics/src/main/java/javafx/scene/Scene.java modules/graphics/src/main/java/javafx/scene/SubScene.java modules/graphics/src/main/native-prism-es2/GLContext.c modules/graphics/src/main/native-prism-es2/PrismES2Defs.h modules/graphics/src/main/native-prism-es2/eglfb/eglUtils.c modules/graphics/src/main/native-prism-es2/ios/IOSGLContext.c modules/graphics/src/main/native-prism-es2/macosx/MacGLContext.c modules/graphics/src/main/native-prism-es2/windows/WinGLContext.c modules/graphics/src/main/native-prism-es2/x11/X11GLContext.c modules/graphics/src/stub/java/com/sun/javafx/pgstub/StubStage.java modules/graphics/src/test/java/com/sun/javafx/sg/prism/TestGraphics.java
diffstat 56 files changed, 746 insertions(+), 218 deletions(-) [+]
line wrap: on
line diff
--- a/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGSubScene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/sg/prism/NGSubScene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -25,6 +25,7 @@
 package com.sun.javafx.sg.prism;
 
 import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.prism.CompositeMode;
 import com.sun.prism.Graphics;
 import com.sun.prism.RTTexture;
 import com.sun.prism.ResourceFactory;
@@ -41,8 +42,24 @@
 
     private int rtWidth, rtHeight;
     private RTTexture rtt;
+    // ressolveRTT is a temporary render target to "resolve" a msaa render buffer
+    // into a normal color render target.
+    // REMIND: resolveRTT could be a single shared scratch rtt
+    private RTTexture resolveRTT = null;
     private NGNode root = null;
     private boolean renderSG = true;
+    // Depth and antiAliasing are immutable states
+    private final boolean depthBuffer;
+    private final boolean antiAliasing;
+
+    public NGSubScene(boolean depthBuffer, boolean antiAliasing) {
+        this.depthBuffer = depthBuffer;
+        this.antiAliasing = antiAliasing;
+    }
+
+    private NGSubScene() {
+        this(false, false);
+    }
 
     public void setRoot(NGNode root) {
         this.root = root;
@@ -80,11 +97,6 @@
         }
     }
 
-    boolean depthBuffer = false;
-    public void setDepthBuffer(boolean depthBuffer) {
-        this.depthBuffer = depthBuffer;
-    }
-
     private Object lights[];
 
     public Object[] getLights() { return lights; }
@@ -124,18 +136,24 @@
         return false;
     }
 
+    private boolean isOpaque = false;
     private void applyBackgroundFillPaint(Graphics g) {
+        isOpaque = true;
         if (fillPaint != null) {
             if (fillPaint instanceof Color) {
-                g.clear((Color)fillPaint);
+                Color fillColor = (Color)fillPaint;
+                isOpaque = (fillColor.getAlpha() >= 1.0);
+                g.clear(fillColor);
             } else {
                 if (!fillPaint.isOpaque()) {
                     g.clear();
+                    isOpaque = false;
                 }
                 g.setPaint(fillPaint);
                 g.fillRect(0, 0, rtt.getContentWidth(), rtt.getContentHeight());
             }
         } else {
+            isOpaque = false;
             // Default is transparent
             g.clear();
         }
@@ -155,7 +173,8 @@
             if (rtt == null) {
                 ResourceFactory factory = g.getResourceFactory();
                 rtt = factory.createRTTexture(rtWidth, rtHeight,
-                                              Texture.WrapMode.CLAMP_NOT_NEEDED);
+                                              Texture.WrapMode.CLAMP_NOT_NEEDED,
+                                              antiAliasing);
             }
             Graphics rttGraphics = rtt.createGraphics();
             rttGraphics.setLights(lights);
@@ -171,8 +190,44 @@
             root.clearDirtyTree();
             renderSG = false;
         }
-        g.drawTexture(rtt, rtt.getContentX(), rtt.getContentY(),
-                      rtt.getContentWidth(), rtt.getContentHeight());
+        if (antiAliasing) {
+            int x0 = rtt.getContentX();
+            int y0 = rtt.getContentY();
+            int x1 = x0 + rtt.getContentWidth();
+            int y1 = y0 + rtt.getContentHeight();
+            if ((isOpaque || g.getCompositeMode() == CompositeMode.SRC)
+                    && g.getTransformNoClone().isTranslateOrIdentity()) {
+                // Round translation to closest pixel
+                int tx = (int)(g.getTransformNoClone().getMxt() + 0.5);
+                int ty = (int)(g.getTransformNoClone().getMyt() + 0.5);
+                // Blit SubScene directly to scene surface
+                g.blit(rtt, null, x0, y0, x1, y1,
+                            x0 + tx, y0 + ty, x1 + tx, y1 + ty);
+            } else {
+                if (resolveRTT != null &&
+                        (resolveRTT.getContentWidth() < rtt.getContentWidth() ||
+                        (resolveRTT.getContentHeight() < rtt.getContentHeight())))
+                {
+                    // If msaa rtt is larger than resolve buffer, then dispose
+                    resolveRTT.dispose();
+                    resolveRTT = null;
+                }
+                if (resolveRTT == null || resolveRTT.isSurfaceLost()) {
+                    resolveRTT = g.getResourceFactory().createRTTexture(rtWidth, rtHeight,
+                            Texture.WrapMode.CLAMP_NOT_NEEDED, false);
+                } else {
+                    resolveRTT.lock();
+                }
+                g.blit(rtt, resolveRTT, x0, y0, x1, y1,
+                        x0, y0, x1, y1);
+                g.drawTexture(resolveRTT, rtt.getContentX(), rtt.getContentY(),
+                        rtt.getContentWidth(), rtt.getContentHeight());
+                resolveRTT.unlock();
+            }
+        } else {
+            g.drawTexture(rtt, rtt.getContentX(), rtt.getContentY(),
+                          rtt.getContentWidth(), rtt.getContentHeight());
+        }
         rtt.unlock();
     }
 
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java	Thu Jul 11 15:02:05 2013 -0700
@@ -390,4 +390,4 @@
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-}
+    }
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/TKStage.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/TKStage.java	Thu Jul 11 15:02:05 2013 -0700
@@ -53,7 +53,7 @@
      *
      * @return scenePeer The peer of the scene to be displayed
      */
-    public TKScene createTKScene(boolean depthBuffer);
+    public TKScene createTKScene(boolean depthBuffer, boolean antiAliasing);
 
     /**
      * Set the scene to be displayed in this stage
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -58,8 +58,8 @@
 
     private final EmbeddedSceneDnD dndDelegate;
 
-    public EmbeddedScene(HostInterface host, boolean depthBuffer) {
-        super(depthBuffer);
+    public EmbeddedScene(HostInterface host, boolean depthBuffer, boolean antiAliasing) {
+        super(depthBuffer, antiAliasing);
         sceneState = new EmbeddedState(this);
 
         this.host = host;
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedStage.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedStage.java	Thu Jul 11 15:02:05 2013 -0700
@@ -50,8 +50,8 @@
     // TKStage methods
 
     @Override
-    public TKScene createTKScene(boolean depthBuffer) {
-        return new EmbeddedScene(host, depthBuffer);
+    public TKScene createTKScene(boolean depthBuffer, boolean antiAliasing) {
+        return new EmbeddedScene(host, depthBuffer, antiAliasing);
     }
 
     @Override
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedState.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedState.java	Thu Jul 11 15:02:05 2013 -0700
@@ -40,7 +40,7 @@
     public EmbeddedState(GlassScene vs) {
         super(vs);
     }
-    
+
     /**
      * Put the pixels on the screen.
      * 
@@ -58,7 +58,7 @@
             }
         }
     }
-    
+
     /**
      * Drawing can occur when there is an embedded scene that has a host.
      *
@@ -70,7 +70,7 @@
         EmbeddedScene escene = (EmbeddedScene) scene;
         return escene != null && escene.host != null && getWidth() > 0 && getHeight() > 0;
     }
-    
+
     /** Updates the state of this object based on the current state of its
      * nativeWindow.
      *
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -72,13 +72,15 @@
     private boolean doPresent = true;
     private final AtomicBoolean painting = new AtomicBoolean(false);
 
-    private boolean depthBuffer = false;
+    private final boolean depthBuffer;
+    private final boolean antiAliasing;
 
     SceneState sceneState;
 
     private AccessControlContext accessCtrlCtx = null;
 
-    protected GlassScene(boolean depthBuffer) {
+    protected GlassScene(boolean depthBuffer, boolean antiAliasing) {
+        this.antiAliasing = antiAliasing;
         this.depthBuffer = depthBuffer;
         sceneState = new SceneState(this);
     }
@@ -127,6 +129,10 @@
         return depthBuffer;
     }
 
+    boolean isAntiAliasing() {
+        return antiAliasing;
+    }
+
     protected abstract boolean isSynchronous();
 
     @Override public void setTKSceneListener(final TKSceneListener listener) {
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/PopupScene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/PopupScene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -27,8 +27,8 @@
 
 final class PopupScene extends ViewScene {
 
-    public PopupScene(boolean depthBuffer) {
-        super(depthBuffer);
+    public PopupScene(boolean depthBuffer, boolean antiAliasing) {
+        super(depthBuffer, antiAliasing);
     }
 
     private PopupStage getPopupStage() {
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/PopupStage.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/PopupStage.java	Thu Jul 11 15:02:05 2013 -0700
@@ -56,8 +56,8 @@
     }
 
     @Override
-    public TKScene createTKScene(boolean depthBuffer) {
-        return new PopupScene(depthBuffer);
+    public TKScene createTKScene(boolean depthBuffer, boolean antiAliasing) {
+        return new PopupScene(depthBuffer, antiAliasing);
     }
 
     public void setResizable(final boolean resizable) {
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/SceneState.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/SceneState.java	Thu Jul 11 15:02:05 2013 -0700
@@ -50,6 +50,11 @@
         scene = vs;
     }
 
+    @Override
+    public boolean isAntiAliasing() {
+        return scene.isAntiAliasing();
+    }
+
     /**
      * Returns the glass scene for the view state
      * .
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewScene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewScene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -45,8 +45,8 @@
 
     private PaintRenderJob paintRenderJob;
 
-    public ViewScene(boolean depthBuffer) {
-        super(depthBuffer);
+    public ViewScene(boolean depthBuffer, boolean antiAliasing) {
+        super(depthBuffer, antiAliasing);
 
         this.platformView = Application.GetApplication().createView();
         this.platformView.setEventHandler(new GlassViewEventHandler(this));
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java	Thu Jul 11 15:02:05 2013 -0700
@@ -174,8 +174,8 @@
         return style;
     }
 
-    @Override public TKScene createTKScene(boolean depthBuffer) {
-        return new ViewScene(depthBuffer);
+    @Override public TKScene createTKScene(boolean depthBuffer, boolean antiAliasing) {
+        return new ViewScene(depthBuffer, antiAliasing);
     }
     
     /**
--- a/modules/graphics/src/main/java/com/sun/prism/Graphics.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/Graphics.java	Thu Jul 11 15:02:05 2013 -0700
@@ -146,6 +146,9 @@
     public void drawString(GlyphList gl, FontStrike strike, float x, float y,
                            Color selectColor, int selectStart, int selectEnd);
 
+    public void blit(RTTexture srcTex, RTTexture dstTex,
+                     int srcX0, int srcY0, int srcX1, int srcY1,
+                     int dstX0, int dstY0, int dstX1, int dstY1);
     public void drawTexture(Texture tex, float x, float y, float w, float h);
     public void drawTexture(Texture tex,
                             float dx1, float dy1, float dx2, float dy2,
--- a/modules/graphics/src/main/java/com/sun/prism/GraphicsPipeline.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/GraphicsPipeline.java	Thu Jul 11 15:02:05 2013 -0700
@@ -75,6 +75,8 @@
 
     public abstract boolean is3DSupported();
 
+    public boolean isAntiAliasingSupported() { return false; }
+
     public abstract boolean isVsyncSupported();
 
     /**
--- a/modules/graphics/src/main/java/com/sun/prism/PresentableState.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/PresentableState.java	Thu Jul 11 15:02:05 2013 -0700
@@ -200,6 +200,8 @@
         return window;
     }
 
+    public boolean isAntiAliasing() { return false; }
+
     /**
      * @return the underlying View
      *
--- a/modules/graphics/src/main/java/com/sun/prism/RenderTarget.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/RenderTarget.java	Thu Jul 11 15:02:05 2013 -0700
@@ -32,4 +32,5 @@
     public Graphics createGraphics();
     public boolean isOpaque();
     public void setOpaque(boolean opaque);
+    public boolean isAntiAliasing();
 }
--- a/modules/graphics/src/main/java/com/sun/prism/ResourceFactory.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/ResourceFactory.java	Thu Jul 11 15:02:05 2013 -0700
@@ -155,6 +155,7 @@
     public Texture createMaskTexture(int width, int height, Texture.WrapMode wrapMode);
     public Texture createFloatTexture(int width, int height);
     public RTTexture createRTTexture(int width, int height, Texture.WrapMode wrapMode);
+    public RTTexture createRTTexture(int width, int height, Texture.WrapMode wrapMode, boolean antiAliasing);
     public Presentable createPresentable(PresentableState pState);
     public VertexBuffer createVertexBuffer(int maxQuads);
 
@@ -167,7 +168,7 @@
     public void removeFactoryListener(ResourceFactoryListener l);
 
     public void dispose();
-    
+
     /*
      * 3D stuff
      */
--- a/modules/graphics/src/main/java/com/sun/prism/d3d/D3DContext.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/d3d/D3DContext.java	Thu Jul 11 15:02:05 2013 -0700
@@ -32,6 +32,7 @@
 import com.sun.javafx.geom.transform.GeneralTransform3D;
 import com.sun.prism.CompositeMode;
 import com.sun.prism.MeshView;
+import com.sun.prism.RTTexture;
 import com.sun.prism.RenderTarget;
 import com.sun.prism.Texture;
 import com.sun.prism.camera.PrismCameraImpl;
@@ -499,4 +500,11 @@
         updateWorldTransform(transformNoClone);
         nRenderMeshView(pContext, nativeMeshView);
     }
+
+    @Override
+    public void blit(RTTexture srcRTT, RTTexture dstRTT,
+                     int srcX0, int srcY0, int srcX1, int srcY1,
+                     int dstX0, int dstY0, int dstX1, int dstY1) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/d3d/D3DPipeline.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/d3d/D3DPipeline.java	Thu Jul 11 15:02:05 2013 -0700
@@ -238,6 +238,12 @@
     }
 
     @Override
+    public boolean isAntiAliasingSupported() {
+        //TODO: 3D - Add AA support for D3D
+        return false;
+    }
+
+    @Override
     public boolean isVsyncSupported() {
         return true;
     }
--- a/modules/graphics/src/main/java/com/sun/prism/d3d/D3DRTTexture.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/d3d/D3DRTTexture.java	Thu Jul 11 15:02:05 2013 -0700
@@ -159,4 +159,9 @@
     public boolean isVolatile() {
         return getContext().isRTTVolatile();
     }
+
+    public boolean isAntiAliasing() {
+        //TODO: 3D - Add AA support for D3D
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/d3d/D3DResourceFactory.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/d3d/D3DResourceFactory.java	Thu Jul 11 15:02:05 2013 -0700
@@ -252,6 +252,11 @@
 
     @Override
     public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode) {
+        return createRTTexture(width, height, wrapMode, false);
+    }
+
+    @Override
+    public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean antiAliasing) {
         if (PrismSettings.verbose && context.isLost()) {
             System.err.println("RT Texture allocation while the device is lost");
         }
@@ -301,7 +306,7 @@
         if (pResource != 0L) {
             int width = D3DResourceFactory.nGetTextureWidth(pResource);
             int height = D3DResourceFactory.nGetTextureHeight(pResource);
-            D3DRTTexture rtt = createRTTexture(width, height, WrapMode.CLAMP_NOT_NEEDED);
+            D3DRTTexture rtt = createRTTexture(width, height, WrapMode.CLAMP_NOT_NEEDED, pState.isAntiAliasing());
 
             if (rtt != null) {
                 return new D3DSwapChain(context, pResource, rtt);
--- a/modules/graphics/src/main/java/com/sun/prism/d3d/D3DSwapChain.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/d3d/D3DSwapChain.java	Thu Jul 11 15:02:05 2013 -0700
@@ -135,4 +135,9 @@
     public void setOpaque(boolean opaque) {
         texBackBuffer.setOpaque(opaque);
     }
+
+    public boolean isAntiAliasing() {
+        //TODO: 3D - Add AA support for D3D
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/es2/ES2Context.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/ES2Context.java	Thu Jul 11 15:02:05 2013 -0700
@@ -31,11 +31,11 @@
 import com.sun.javafx.geom.Rectangle;
 import com.sun.javafx.geom.Vec3d;
 import com.sun.javafx.geom.transform.Affine2D;
-import com.sun.javafx.geom.transform.Affine3D;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.prism.CompositeMode;
 import com.sun.prism.Material;
 import com.sun.prism.PixelFormat;
+import com.sun.prism.RTTexture;
 import com.sun.prism.RenderTarget;
 import com.sun.prism.Texture;
 import com.sun.prism.camera.PrismCameraImpl;
@@ -211,9 +211,14 @@
         int fboID = ((ES2RenderTarget)target).getFboID();
         glContext.bindFBO(fboID);
 
-        if (depthTest && target instanceof ES2RTTexture) {
+        boolean antiAliasing = false;
+        if (target instanceof ES2RTTexture) {
             // Attach a depth buffer to the currently bound FBO
-            ((ES2RTTexture) target).attachDepthBuffer(this);
+            ES2RTTexture rtTarget = (ES2RTTexture)target;
+            antiAliasing = rtTarget.isAntiAliasing();
+            if (depthTest) {
+                rtTarget.attachDepthBuffer(this);
+            }
         }
 
         // update viewport
@@ -222,6 +227,7 @@
         int w = target.getContentWidth();
         int h = target.getContentHeight();
         glContext.updateViewportAndDepthTest(x, y, w, h, depthTest);
+        glContext.updateMSAAState(antiAliasing);
 
         if (camera instanceof PrismDefaultCamera) {
             // update projection matrix; this will be uploaded to the shader
@@ -460,6 +466,19 @@
         glContext.setPointLight(nativeHandle, index, x, y, z, r, g, b, w);
     }
 
+    @Override
+    public void blit(RTTexture rtt, RTTexture dstRTT,
+                     int srcX0, int srcY0, int srcX1, int srcY1,
+                     int dstX0, int dstY0, int dstX1, int dstY1)
+    {
+        // If dstRTT is null then will blit to currently bound fbo
+        int dstFboID = dstRTT == null ? 0 : ((ES2RTTexture)dstRTT).getFboID();
+        int srcFboID = ((ES2RTTexture)rtt).getFboID();
+        glContext.blitFBO(srcFboID, dstFboID,
+                          srcX0, srcY0, srcX1, srcY1,
+                          dstX0, dstY0, dstX1, dstY1);
+    }
+
     void renderMeshView(long nativeHandle, BaseTransform xform, ES2MeshView meshView) {
 
         ES2Shader shader = (ES2Shader) getPhongShader(meshView);
--- a/modules/graphics/src/main/java/com/sun/prism/es2/ES2Pipeline.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/ES2Pipeline.java	Thu Jul 11 15:02:05 2013 -0700
@@ -49,6 +49,7 @@
     public static final GLFactory glFactory;
     public static final GLPixelFormat.Attributes
             pixelFormatAttributes = new GLPixelFormat.Attributes();
+    static final boolean antiAliasingSupported;
     private static boolean es2Enabled;
 
     static {
@@ -96,6 +97,7 @@
         if (PrismSettings.verbose && isEmbededDevice) {
             System.out.println("ES2Pipeline: OpenGL ES 2.0 embedded device detected");
         }
+        antiAliasingSupported = (glFactory.isGLExtensionSupported("GL_ARB_multisample"));
     }
     private static Thread creator;
     private static final ES2Pipeline theInstance;
@@ -199,6 +201,11 @@
     }
 
     @Override
+    public final boolean isAntiAliasingSupported() {
+        return antiAliasingSupported;
+    }
+
+    @Override
     public boolean isVsyncSupported() {
         return true;
     }
--- a/modules/graphics/src/main/java/com/sun/prism/es2/ES2RTTexture.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/ES2RTTexture.java	Thu Jul 11 15:02:05 2013 -0700
@@ -66,14 +66,35 @@
         if (dbID != 0) {
             return;
         }
+        int msaaSamples = isAntiAliasing() ? context.getGLContext().getSampleSize() : 0;
         dbID = context.getGLContext().createDepthBuffer(getPhysicalWidth(),
-                getPhysicalHeight());
+                getPhysicalHeight(), msaaSamples);
 
         // Add to disposer record so that we can cleanup the depth buffer when
         // this RTT is destroyed.
         texData.setDepthBufferID(dbID);
     }
 
+    /**
+     * Create and attach a color multisample render buffer to current FBO
+     * @param context current context
+     */
+    private void createAndAttachMSAABuffer(ES2Context context) {
+        // Assert isAntiAliasing() must be true
+        ES2RTTextureData texData = resource.getResource();
+        int rbID = texData.getMSAARenderBufferID();
+        if (rbID != 0) {
+            return;
+        }
+        GLContext glContext = context.getGLContext();
+        rbID = glContext.createRenderBuffer(getPhysicalWidth(), 
+                getPhysicalHeight(), glContext.getSampleSize());
+
+        // TODO: 3D - Add to disposer record so that we can cleanup the
+        // render buffer when this RTT is destroyed.
+        texData.setMSAARenderBufferID(rbID);
+    }
+
     static int getCompatibleDimension(ES2Context context, int dim, WrapMode wrapMode) {
         GLContext glContext = context.getGLContext();
         boolean pad;
@@ -129,7 +150,7 @@
         return pad ? texDim - 2 : texDim;
     }
 
-    static ES2RTTexture create(ES2Context context, int w, int h, WrapMode wrapMode) {
+    static ES2RTTexture create(ES2Context context, int w, int h, WrapMode wrapMode, boolean msaa) {
         // Normally we would use GL_CLAMP_TO_BORDER with a transparent border
         // color to implement our CLAMP_TO_ZERO edge mode, but unfortunately
         // that mode is not available in OpenGL ES.  The workaround is to pad
@@ -231,14 +252,22 @@
 
         // save current texture
         glContext.setActiveTextureUnit(0);
+        int savedFBO = glContext.getBoundFBO();
         int savedTex = glContext.getBoundTexture();
 
-        int nativeTexID = glContext.createTexture(texWidth, texHeight);
+        int nativeTexID = 0;
+        if (!msaa) {
+            // TODO Mac and some other platforms do not support multisample texture
+            // thus forced to skip texture creation below, and rather create a
+            // msaa render buffer
+            nativeTexID = glContext.createTexture(texWidth, texHeight);
+        }
 
         int nativeFBOID = 0;
-        if (nativeTexID != 0) {
-            // Create FBO (this method will save and restore the previous bound FBO)
-            nativeFBOID = glContext.createFBO(nativeTexID, texWidth, texHeight);
+        if (nativeTexID != 0 || msaa) {
+            // Create FBO (this method will generate and bind a new FBO,
+            // and if texture is valid, attach texture as color attribute)
+            nativeFBOID = glContext.createFBO(nativeTexID);
             if (nativeFBOID == 0) {
                 glContext.deleteTexture(nativeTexID);
                 nativeTexID = 0;
@@ -246,16 +275,20 @@
         }
         ES2RTTextureData texData =
             new ES2RTTextureData(context, nativeTexID, nativeFBOID, size);
-        ES2TextureResource<ES2RTTextureData> texRes =
-            new ES2TextureResource<ES2RTTextureData>(texData);
+        ES2TextureResource<ES2RTTextureData> texRes = new ES2TextureResource<ES2RTTextureData>(texData);
 
-        // restore previous texture
-        glContext.setBoundTexture(savedTex);
-
-        return new ES2RTTexture(context, texRes, wrapMode,
+        ES2RTTexture es2RTT = new ES2RTTexture(context, texRes, wrapMode,
                                 texWidth, texHeight,
                                 contentX, contentY,
                                 contentW, contentH);
+        if (msaa) {
+            es2RTT.createAndAttachMSAABuffer(context);
+        }
+        // Restore previous FBO
+        glContext.bindFBO(savedFBO);
+        // restore previous texture
+        glContext.setBoundTexture(savedTex);
+        return es2RTT;
     }
 
     public Texture getBackBuffer() {
@@ -273,26 +306,24 @@
     public boolean readPixels(Buffer pixels, int x, int y, int width, int height) {
         context.flushVertexBuffer();
         GLContext glContext = context.getGLContext();
-        return glContext.readPixels(pixels, x, y, width, height);
-    }
-
-    public boolean readPixels(Buffer pixels) {
-        context.flushVertexBuffer();
-        GLContext glContext = context.getGLContext();
         int id = glContext.getBoundFBO();
         int fboID = getFboID();
         boolean changeBoundFBO = id != fboID;
         if (changeBoundFBO) {
             glContext.bindFBO(fboID);
         }
-        boolean result = glContext.readPixels(pixels, getContentX(), getContentY(),
-                 getContentWidth(), getContentHeight());                
+        boolean result = glContext.readPixels(pixels, x, y, width, height);
         if (changeBoundFBO) {
             glContext.bindFBO(id);
         }
         return result;
     }
 
+    public boolean readPixels(Buffer pixels) {
+        return readPixels(pixels, getContentX(), getContentY(),
+                 getContentWidth(), getContentHeight());
+    }
+
     public int getFboID() {
         return resource.getResource().getFboID();
     }
@@ -340,4 +371,8 @@
     public boolean isVolatile() {
         return false;
     }
+
+    public boolean isAntiAliasing() {
+        return resource.getResource().getMSAARenderBufferID() != 0;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/es2/ES2RTTextureData.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/ES2RTTextureData.java	Thu Jul 11 15:02:05 2013 -0700
@@ -30,6 +30,7 @@
 class ES2RTTextureData extends ES2TextureData {
     private int fboID;
     private int dbID;
+    private int rbID;
 
     ES2RTTextureData(ES2Context context, int texID, int fboID, long size) {
         super(context, texID, size);
@@ -40,6 +41,16 @@
         return fboID;
     }
 
+    public int getMSAARenderBufferID() {
+        return this.rbID;
+    }
+
+    void setMSAARenderBufferID(int rbID) {
+        // Texture ID and multisample render buffer are mutually excusive
+        assert getTexID() == 0;
+        this.rbID = rbID;
+    }
+
     public int getDepthBufferID() {
         return dbID;
     }
--- a/modules/graphics/src/main/java/com/sun/prism/es2/ES2ResourceFactory.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/ES2ResourceFactory.java	Thu Jul 11 15:02:05 2013 -0700
@@ -105,7 +105,11 @@
     }
 
     public RTTexture createRTTexture(int width, int height, WrapMode wrapMode) {
-        return ES2RTTexture.create(context, width, height, wrapMode);
+        return createRTTexture(width, height, wrapMode, false);
+    }
+
+    public RTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean antiAliasing) {
+        return ES2RTTexture.create(context, width, height, wrapMode, antiAliasing);
     }
 
     public boolean isFormatSupported(PixelFormat format) {
--- a/modules/graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java	Thu Jul 11 15:02:05 2013 -0700
@@ -50,6 +50,7 @@
     // a value of zero corresponds to the windowing system-provided
     // framebuffer object
     int nativeDestHandle = 0;
+    private final boolean antiAliasing;
     /**
      * An offscreen surface that acts as a persistent backbuffer, currently
      * only used when dirty region optimizations are enabled in the scenegraph.
@@ -102,6 +103,7 @@
         this.pixelScaleFactor = PrismSettings.allowHiDPIScaling
                                 ? pState.getScale() //TODO fix getScale
                                 : 1.0f;
+        this.antiAliasing = pState.isAntiAliasing();
         drawable = null;
         if (pState != null) {
             long nativeWindow = pState.getNativeWindow();
@@ -131,15 +133,26 @@
                     needsResize = false;
                 }
                 // Copy (not blend) the stableBackbuffer into place.
-                if (clip == null || copyFullBuffer) {
-                    w = getPhysicalWidth();
-                    h = getPhysicalHeight();
-                    drawTexture(g, stableBackbuffer, 0, 0, w, h, 0, 0, w, h);
-                    copyFullBuffer = false;
+                //TODO: Determine why w/h is needed here
+                w = getPhysicalWidth();
+                h = getPhysicalHeight();
+                Rectangle rectDST = new Rectangle(0, 0, w, h);
+                if (clip != null && !copyFullBuffer) {
+                    rectDST.intersectWith(clip);
+                }
+                copyFullBuffer = false;
+                int x0 = rectDST.x;
+                int y0 = rectDST.y;
+                int x1 = x0 + rectDST.width;
+                int y1 = y0 + rectDST.height;
+                if (isAntiAliasing()) {
+                    context.flushVertexBuffer();
+                    // Note must flip the z axis during blit
+                    context.blit(stableBackbuffer, null, x0, y0, x1, y1,
+                            x0, y1, x1, y0);
                 } else {
-                    drawTexture(g, stableBackbuffer,
-                            clip.x, clip.y, clip.x + clip.width, clip.y + clip.height,
-                            clip.x, clip.y, clip.x + clip.width, clip.y + clip.height);
+                    drawTexture(g, stableBackbuffer, x0, y0, x1, y1,
+                            x0, y0, x1, y1);
                 }
                 stableBackbuffer.unlock();
             }
@@ -204,8 +217,9 @@
             w = getPhysicalWidth();
             h = getPhysicalHeight();
             ResourceFactory factory = context.getResourceFactory();
-            stableBackbuffer =
-                    factory.createRTTexture(w, h, WrapMode.CLAMP_NOT_NEEDED);
+            stableBackbuffer = factory.createRTTexture(w, h,
+                                                       WrapMode.CLAMP_NOT_NEEDED,
+                                                       antiAliasing);
             copyFullBuffer = true;
         }
         ES2Graphics g = ES2Graphics.create(context, stableBackbuffer);
@@ -269,4 +283,9 @@
             stableBackbuffer = null;
         }
     }
+
+    public boolean isAntiAliasing() {
+        return stableBackbuffer != null ? stableBackbuffer.isAntiAliasing() :
+                antiAliasing;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/es2/GLContext.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/es2/GLContext.java	Thu Jul 11 15:02:05 2013 -0700
@@ -109,6 +109,8 @@
     private int[] boundTextures = new int[4];
     // depthTest is initialized to false in the native initState method
     private boolean depthTest = false;
+    private boolean msaa = false;
+    private int maxSampleSize = -1;
 
     private static native void nActiveTexture(long nativeCtxInfo, int texUnit);
     private static native void nBindFBO(long nativeCtxInfo, int nativeFBOID);
@@ -120,7 +122,9 @@
     private static native int nCompileShader(long nativeCtxInfo, String src,
             boolean vertex);
     private static native int nCreateDepthBuffer(long nativeCtxInfo, int width,
-            int height);
+            int height, int msaaSamples);
+    private static native int nCreateRenderBuffer(long nativeCtxInfo, int width,
+            int height, int msaaSamples);
     private static native int nCreateFBO(long nativeCtxInfo, int texID);
     private static native int nCreateProgram(long nativeCtxInfo,
             int vertexShaderID, int[] fragmentShaderID,
@@ -136,6 +140,7 @@
     private static native void nFinish();
     private static native int nGenAndBindTexture();
     private static native int nGetFBO();
+    private static native int nGetMaxSampleSize();
     private static native int nGetMaxTextureSize();
     private static native int nGetUniformLocation(long nativeCtxInfo,
             int programID, String name);
@@ -148,6 +153,7 @@
     private static native void nScissorTest(long nativeCtxInfo, boolean enable,
             int x, int y, int w, int h);
     private static native void nSetDepthTest(long nativeCtxInfo, boolean depthTest);
+    private static native void nSetMSAA(long nativeCtxInfo, boolean msaa);
     private static native void nTexParamsMinMax(int pname);
     private static native boolean nTexImage2D0(int target, int level, int internalFormat,
             int width, int height, int border, int format,
@@ -229,6 +235,9 @@
     private static native void nSetPointLight(long nativeCtxInfo, long nativeMeshViewInfo,
             int index, float x, float y, float z, float r, float g, float b, float w);
     private static native void nRenderMeshView(long nativeCtxInfo, long nativeMeshViewInfo);
+    private static native int  nBlit(long nativeCtxInfo, int srcFBO, int dstFBO,
+            int srcX0, int srcY0, int srcX1, int srcY1,
+            int dstX0, int dstY0, int dstX1, int dstY1);
 
     void activeTexture(int texUnit) {
         nActiveTexture(nativeCtxInfo, texUnit);
@@ -291,11 +300,21 @@
         return nCompileShader(nativeCtxInfo, shaderSource, vertex);
     }
 
-    int createDepthBuffer(int width, int height) {
-        return nCreateDepthBuffer(nativeCtxInfo, width, height);
+    int createDepthBuffer(int width, int height, int msaaSamples) {
+        return nCreateDepthBuffer(nativeCtxInfo, width, height, msaaSamples);
     }
 
-    int createFBO(int texID, int width, int height) {
+    int createRenderBuffer(int width, int height, int msaaSamples) {
+        return nCreateRenderBuffer(nativeCtxInfo, width, height, msaaSamples);
+    }
+
+    /**
+     * Will create FBO by generate new FBO and binding it.
+     * Note: Will not restore previously bound FBO.
+     * @param texID if defined, will attach texture to generated FBO
+     * @return FBO id
+     */
+    int createFBO(int texID) {
         return nCreateFBO(nativeCtxInfo, texID);
     }
 
@@ -347,6 +366,15 @@
         nDeleteShader(nativeCtxInfo, shadeID);
     }
 
+    void blitFBO(int msaaFboID, int dstFboID,
+                 int srcX0, int srcY0, int srcX1, int srcY1,
+                 int dstX0, int dstY0, int dstX1, int dstY1)
+    {
+        nBlit(nativeCtxInfo, msaaFboID, dstFboID,
+              srcX0, srcY0, srcX1, srcY1,
+              dstX0, dstY0, dstX1, dstY1);
+    }
+
     void deleteTexture(int tID) {
         nDeleteTexture(nativeCtxInfo, tID);
     }
@@ -426,6 +454,19 @@
     }
     /***********************************************************/
 
+    int getSampleSize() {
+        int maxSamples = getMaxSampleSize();
+        return maxSamples < 2 ? 0 : (maxSamples < 4 ? 2 : 4);
+    }
+
+    int getMaxSampleSize() {
+        if (maxSampleSize > -1) {
+            return maxSampleSize;
+        }
+        maxSampleSize = ES2Pipeline.antiAliasingSupported ? nGetMaxSampleSize() : 0;
+        return maxSampleSize;
+    }
+
     int getMaxTextureSize() {
         if (maxTextureSize > -1) {
             return maxTextureSize;
@@ -524,6 +565,13 @@
         }
     }
 
+    void updateMSAAState(boolean msaa) {
+        if (this.msaa != msaa) {
+            nSetMSAA(nativeCtxInfo, msaa);
+            this.msaa = msaa;
+        }
+    }
+
     void updateFilterState(int texID, boolean linearFilter) {
         nUpdateFilterState(nativeCtxInfo, texID, linearFilter);
     }
--- a/modules/graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderContext.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderContext.java	Thu Jul 11 15:02:05 2013 -0700
@@ -715,10 +715,14 @@
         }
     }
 
+    abstract public void blit(RTTexture srcRTT, RTTexture dstRTT,
+                          int srcX0, int srcY0, int srcX1, int srcY1,
+                          int dstX0, int dstY0, int dstX1, int dstY1);
+
     @Override
     protected void setRenderTarget(RenderTarget target, PrismCameraImpl camera,
             boolean depthTest, boolean state3D)
-    {        
+    {
         if (target instanceof Texture) {
             ((Texture) target).assertLocked();
         }
--- a/modules/graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderGraphics.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderGraphics.java	Thu Jul 11 15:02:05 2013 -0700
@@ -1580,6 +1580,18 @@
                   bs.getMiterLimit() >= SQRT_2)));
     }
 
+    public void blit(RTTexture srcTex, RTTexture dstTex,
+                     int srcX0, int srcY0, int srcX1, int srcY1,
+                     int dstX0, int dstY0, int dstX1, int dstY1) {
+        if (dstTex == null) {
+            context.setRenderTarget(this);
+        } else {
+            context.setRenderTarget((BaseGraphics)dstTex.createGraphics());
+        }
+        context.blit(srcTex, dstTex, srcX0, srcY0, srcX1, srcY1,
+                dstX0, dstY0, dstX1, dstY1);
+    }
+
     public void drawRect(float x, float y, float w, float h) {
         if (w < 0 || h < 0) {
             return;
@@ -1908,7 +1920,8 @@
         // FontStrike supports LCD, SRC_OVER CompositeMode and Paint is a COLOR
         boolean lcdSupported = blendMode == CompositeMode.SRC_OVER &&
                                textColor != null &&
-                               xform.is2D();
+                               xform.is2D() &&
+                               !getRenderTarget().isAntiAliasing();
 
         /* If the surface can't support LCD text we need to replace an
          * LCD mode strike with the equivalent grey scale one.
--- a/modules/graphics/src/main/java/com/sun/prism/j2d/J2DPresentable.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/j2d/J2DPresentable.java	Thu Jul 11 15:02:05 2013 -0700
@@ -282,4 +282,8 @@
     public boolean recreateOnResize() {
         return false;
     }
+
+    @Override public boolean isAntiAliasing() {
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/j2d/J2DPrismGraphics.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/j2d/J2DPrismGraphics.java	Thu Jul 11 15:02:05 2013 -0700
@@ -1316,6 +1316,13 @@
     public void setup3DRendering() {
     }
 
+    @Override
+    public void blit(RTTexture srcTex, RTTexture dstTex,
+            int srcX0, int srcY0, int srcX1, int srcY1,
+            int dstX0, int dstY0, int dstX1, int dstY1) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
     private static class AdaptorShape implements java.awt.Shape {
         private Shape prshape;
 
--- a/modules/graphics/src/main/java/com/sun/prism/j2d/J2DRTTexture.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/j2d/J2DRTTexture.java	Thu Jul 11 15:02:05 2013 -0700
@@ -151,4 +151,8 @@
     public boolean isVolatile() {
         return false;
     }
+
+    @Override public boolean isAntiAliasing() {
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/j2d/J2DResourceFactory.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/j2d/J2DResourceFactory.java	Thu Jul 11 15:02:05 2013 -0700
@@ -38,7 +38,6 @@
 import com.sun.prism.Texture.Usage;
 import com.sun.prism.Texture.WrapMode;
 import com.sun.prism.impl.BaseResourceFactory;
-import com.sun.prism.impl.ManagedResource;
 import com.sun.prism.impl.TextureResourcePool;
 import com.sun.prism.impl.VertexBuffer;
 import com.sun.prism.impl.shape.BasicShapeRep;
@@ -90,6 +89,11 @@
         return h;
     }
 
+    @Override
+    public RTTexture createRTTexture(int width, int height, Texture.WrapMode wrapMode, boolean antiAliasing) {
+        return createRTTexture(width, height, wrapMode);
+    }
+
     public RTTexture createRTTexture(int width, int height, WrapMode wrapMode) {
         J2DTexturePool pool = J2DTexturePool.instance;
         long size = pool.estimateRTTextureSize(width, height, false);
--- a/modules/graphics/src/main/java/com/sun/prism/null3d/DummyContext.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/null3d/DummyContext.java	Thu Jul 11 15:02:05 2013 -0700
@@ -29,6 +29,7 @@
 import com.sun.javafx.geom.Rectangle;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.prism.CompositeMode;
+import com.sun.prism.RTTexture;
 import com.sun.prism.RenderTarget;
 import com.sun.prism.Texture;
 import com.sun.prism.camera.PrismCameraImpl;
@@ -50,7 +51,7 @@
     }
 
     @Override
-    protected State updateRenderTarget(RenderTarget target, PrismCameraImpl camera, boolean depthTest)  {
+    protected State updateRenderTarget(RenderTarget target, PrismCameraImpl camera, boolean depthTest) {
         return state;
     }
 
@@ -73,4 +74,10 @@
     @Override
     protected void updateCompositeMode(CompositeMode mode) {
     }
+
+    @Override
+    public void blit(RTTexture srcRTT, RTTexture dstRTT,
+                     int srcX0, int srcY0, int srcX1, int srcY1,
+                     int dstX0, int dstY0, int dstX1, int dstY1) {
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/null3d/DummyRTTexture.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/null3d/DummyRTTexture.java	Thu Jul 11 15:02:05 2013 -0700
@@ -102,4 +102,9 @@
     public boolean isVolatile() {
         return false;
     }
+
+    @Override
+    public boolean isAntiAliasing() {
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/null3d/DummyResourceFactory.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/null3d/DummyResourceFactory.java	Thu Jul 11 15:02:05 2013 -0700
@@ -79,6 +79,11 @@
 
     @Override
     public RTTexture createRTTexture(int width, int height, WrapMode wrapMode) {
+        return createRTTexture(width, height, wrapMode, false);
+    }
+
+    @Override
+    public RTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean antiAliasing) {
         return new DummyRTTexture(context, wrapMode, width, height);
     }
 
--- a/modules/graphics/src/main/java/com/sun/prism/null3d/DummySwapChain.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/null3d/DummySwapChain.java	Thu Jul 11 15:02:05 2013 -0700
@@ -113,4 +113,9 @@
     public void setOpaque(boolean opaque) {
         this.opaque = opaque;
     }
+
+    @Override
+    public boolean isAntiAliasing() {
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/null3d/NULL3DPipeline.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/null3d/NULL3DPipeline.java	Thu Jul 11 15:02:05 2013 -0700
@@ -85,6 +85,11 @@
     }
 
     @Override
+    public boolean isAntiAliasingSupported() {
+        return true;
+    }
+
+    @Override
     public boolean isVsyncSupported() {
         return false;
     }
--- a/modules/graphics/src/main/java/com/sun/prism/sw/SWGraphics.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/sw/SWGraphics.java	Thu Jul 11 15:02:05 2013 -0700
@@ -1030,4 +1030,11 @@
         // Light are not supported by SW pipeline
         return null;
     }
+
+    @Override
+    public void blit(RTTexture srcTex, RTTexture dstTex,
+                    int srcX0, int srcY0, int srcX1, int srcY1,
+                    int dstX0, int dstY0, int dstX1, int dstY1) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/sw/SWPresentable.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/sw/SWPresentable.java	Thu Jul 11 15:02:05 2013 -0700
@@ -93,4 +93,8 @@
     public int getContentHeight() {
         return pState.getHeight();
     }
+
+    @Override public boolean isAntiAliasing() {
+        return super.isAntiAliasing();
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/sw/SWRTTexture.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/sw/SWRTTexture.java	Thu Jul 11 15:02:05 2013 -0700
@@ -137,4 +137,9 @@
     public boolean isVolatile() {
         return false;
     }
+
+    @Override
+    public boolean isAntiAliasing() {
+        return false;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/prism/sw/SWResourceFactory.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/com/sun/prism/sw/SWResourceFactory.java	Thu Jul 11 15:02:05 2013 -0700
@@ -49,7 +49,7 @@
 final class SWResourceFactory
     extends BaseResourceFactory
         implements ResourceFactory {
-            
+
     private static final ShapeRep theRep = new BasicShapeRep();
     private static final ShapeRep rectRep = new BasicRoundRectRep();
 
@@ -96,12 +96,13 @@
     @Override public VertexBuffer createVertexBuffer(int maxQuads) {
         throw new UnsupportedOperationException("createVertexBuffer:unimp");
     }
-            
-    @Override public Presentable createPresentable(PresentableState pstate) {
+
+    
+    @Override public Presentable createPresentable(PresentableState pState) {
         if (PrismSettings.debug) {
             System.out.println("+ SWRF.createPresentable()");
         }
-        return new SWPresentable(pstate, this);
+        return new SWPresentable(pState, this);
     }
 
     public int getRTTWidth(int w, WrapMode wrapMode) {
@@ -112,6 +113,10 @@
         return h;
     }
 
+    @Override public RTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean antiAliasing) {
+        return createRTTexture(width, height, wrapMode);
+    }
+
     @Override public RTTexture createRTTexture(int width, int height,
                                                WrapMode wrapMode)
     {
@@ -126,7 +131,7 @@
     @Override public int getMaximumTextureSize() {
         return Integer.MAX_VALUE;
     }
-            
+
     @Override public boolean isFormatSupported(PixelFormat format) {
         switch (format) {
             case BYTE_RGB:
--- a/modules/graphics/src/main/java/javafx/scene/Node.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/javafx/scene/Node.java	Thu Jul 11 15:02:05 2013 -0700
@@ -1842,7 +1842,7 @@
             Scene s = getScene();
             if (s != null) {
                 params.setCamera(s.getEffectiveCamera());
-                params.setDepthBuffer(s.isDepthBufferInteral());
+                params.setDepthBuffer(s.isDepthBufferInternal());
                 params.setFill(s.getFill());
             }
         }
@@ -1936,7 +1936,7 @@
             Scene s = getScene();
             if (s != null) {
                 params.setCamera(s.getEffectiveCamera());
-                params.setDepthBuffer(s.isDepthBufferInteral());
+                params.setDepthBuffer(s.isDepthBufferInternal());
                 params.setFill(s.getFill());
             }
         } else {
--- a/modules/graphics/src/main/java/javafx/scene/Scene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/javafx/scene/Scene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -190,7 +190,8 @@
     private double widthSetByUser = -1.0;
     private double heightSetByUser = -1.0;
     private boolean sizeInitialized = false;
-    private boolean depthBuffer = false;
+    private final boolean depthBuffer;
+    private final boolean antiAliasing;
 
     private int dirtyBits;
 
@@ -215,7 +216,7 @@
      * @throws NullPointerException if root is null
      */
     public Scene(Parent root) {
-        this(root, -1, -1, Color.WHITE, false);
+        this(root, -1, -1, Color.WHITE, false, false);
     }
 
 //Public constructor initializing public-init properties
@@ -246,7 +247,7 @@
      * @throws NullPointerException if root is null
      */
     public Scene(Parent root, double width, double height) {
-        this(root, width, height, Color.WHITE, false);
+        this(root, width, height, Color.WHITE, false, false);
     }
 
     /**
@@ -260,7 +261,7 @@
      * @throws NullPointerException if root is null
      */
     public Scene(Parent root, @Default("javafx.scene.paint.Color.WHITE") Paint fill) {
-        this(root, -1, -1, fill, false);
+        this(root, -1, -1, fill, false, false);
     }
 
     /**
@@ -277,7 +278,7 @@
      */
     public Scene(Parent root, double width, double height,
             @Default("javafx.scene.paint.Color.WHITE") Paint fill) {
-        this(root, width, height, fill, false);
+        this(root, width, height, fill, false, false);
     }
 
     /**
@@ -301,7 +302,7 @@
      * @see javafx.scene.Node#setDepthTest(DepthTest)
      */
     public Scene(Parent root, @Default("-1") double width, @Default("-1") double height, boolean depthBuffer) {
-        this(root, width, height, Color.WHITE, depthBuffer);
+        this(root, width, height, Color.WHITE, depthBuffer, false);
     }
 
     /**
@@ -330,41 +331,34 @@
     public Scene(Parent root, @Default("-1") double width, @Default("-1") double height,
             boolean depthBuffer, boolean antiAliasing) {
 
-        // TODO: 3D - Support scene anti-aliasing using MSAA.
-        this(root, width, height, Color.WHITE, depthBuffer);
-
-        // NOTE: this block will be removed once implement anti-aliasing
-        if (antiAliasing) {
+        this(root, width, height, Color.WHITE, depthBuffer, antiAliasing);
+
+        if (antiAliasing && !com.sun.prism.GraphicsPipeline.getPipeline().isAntiAliasingSupported())
+        {
             String logname = Scene.class.getName();
-            PlatformLogger.getLogger(logname).warning("3D anti-aliasing is "
-                    + "not supported yet.");
-        }
-
-        if ((depthBuffer || antiAliasing)
-                && !Platform.isSupported(ConditionalFeature.SCENE3D)) {
+            PlatformLogger.getLogger(logname).warning("System can't support "
+                + "antiAliasing");
+        }
+    }
+
+    private Scene(Parent root, double width, double height,
+            @Default("javafx.scene.paint.Color.WHITE") Paint fill,
+            boolean depthBuffer, boolean antiAliasing) {
+        this.depthBuffer = depthBuffer;
+        this.antiAliasing = antiAliasing;
+        if (root == null) {
+            throw new NullPointerException("Root cannot be null");
+        }
+
+        if ((depthBuffer || antiAliasing) && !Platform.isSupported(ConditionalFeature.SCENE3D)) {
             String logname = Scene.class.getName();
             PlatformLogger.getLogger(logname).warning("System can't support "
                     + "ConditionalFeature.SCENE3D");
-            // TODO: 3D - ignore depthBuffer and antiAliasing at rendering time
-        }
-    }
-
-    private Scene(Parent root, double width, double height,
-            @Default("javafx.scene.paint.Color.WHITE") Paint fill,
-            boolean depthBuffer) {
-        if (root == null) {
-            throw new NullPointerException("Root cannot be null");
-        }
-
-        if (depthBuffer && !Platform.isSupported(ConditionalFeature.SCENE3D)) {
-            String logname = Scene.class.getName();
-            PlatformLogger.getLogger(logname).warning("System can't support "
-                    + "ConditionalFeature.SCENE3D");
         }
 
         Toolkit.getToolkit().checkFxUserThread();
         setRoot(root);
-        init(width, height, depthBuffer);
+        init(width, height);
         setFill(fill);
     }
 
@@ -661,9 +655,13 @@
      * Return true if this {@code Scene} is anti-aliased otherwise false.
      * @since JavaFX 8.0
      */
-    public boolean isAntiAliasing() {
-        //TODO: 3D - Implement this method.
-        return false; // For now
+    public final boolean isAntiAliasing() {
+        return antiAliasing;
+    }
+
+    private boolean isAntiAliasingInternal() {
+        return antiAliasing && com.sun.prism.GraphicsPipeline.getPipeline().isAntiAliasingSupported() &&
+                Platform.isSupported(ConditionalFeature.SCENE3D);
     }
 
     /**
@@ -751,7 +749,7 @@
         impl_setAllowPGAccess(true);
 
         Toolkit tk = Toolkit.getToolkit();
-        impl_peer = windowPeer.createTKScene(isDepthBufferInteral());
+        impl_peer = windowPeer.createTKScene(isDepthBufferInternal(), isAntiAliasingInternal());
         PerformanceTracker.logEvent("Scene.initPeer TKScene created");
         impl_peer.setSecurityContext(acc);
         impl_peer.setTKSceneListener(new ScenePeerListener());
@@ -1296,7 +1294,7 @@
         BaseTransform transform = BaseTransform.IDENTITY_TRANSFORM;
 
         return doSnapshot(this, 0, 0, w, h,
-                getRoot(), transform, isDepthBufferInteral(),
+                getRoot(), transform, isDepthBufferInternal(),
                 getFill(), getEffectiveCamera(), img);
     }
 
@@ -1537,14 +1535,14 @@
         return depthBuffer;
     }
 
-    boolean isDepthBufferInteral() {
+    boolean isDepthBufferInternal() {
         if (!Platform.isSupported(ConditionalFeature.SCENE3D)) {
             return false;
         }
         return depthBuffer;
     }
 
-    private void init(double width, double height, boolean depthBuffer) {
+    private void init(double width, double height) {
         if (width >= 0) {
             widthSetByUser = width;
             setWidth((float)width);
@@ -1554,7 +1552,6 @@
             setHeight((float)height);
         }
         sizeInitialized = (widthSetByUser >= 0 && heightSetByUser >= 0);
-        this.depthBuffer = depthBuffer;
         init();
     }
 
--- a/modules/graphics/src/main/java/javafx/scene/SubScene.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/java/javafx/scene/SubScene.java	Thu Jul 11 15:02:05 2013 -0700
@@ -76,9 +76,7 @@
      * @throws NullPointerException if root is null
      */
     public SubScene(Parent root, double width, double height) {
-        setRoot(root);
-        setWidth(width);
-        setHeight(height);
+        this(root, width, height, false, false);
     }
 
     /**
@@ -104,43 +102,53 @@
      * @see javafx.scene.Node#setDepthTest(DepthTest)
      */
     public SubScene(Parent root, double width, double height,
-            boolean depthBuffer, boolean antiAliasing) {
-        this(root, width, height);
+            boolean depthBuffer, boolean antiAliasing)
+    {
         this.depthBuffer = depthBuffer;
+        this.antiAliasing = antiAliasing;
+        setRoot(root);
+        setWidth(width);
+        setHeight(height);
 
-        // NOTE: this block will be removed once implement anti-aliasing
-        if (antiAliasing) {
-            String logname = SubScene.class.getName();
-            PlatformLogger.getLogger(logname).warning("3D anti-aliasing is "
-                    + "not supported yet.");
-        }
-
-        if ((depthBuffer || antiAliasing)
-                && !Platform.isSupported(ConditionalFeature.SCENE3D)) {
+        if ((depthBuffer || antiAliasing) && !is3DSupported) {
             String logname = SubScene.class.getName();
             PlatformLogger.getLogger(logname).warning("System can't support "
                     + "ConditionalFeature.SCENE3D");
-            // TODO: 3D - ignore depthBuffer and antiAliasing at rendering time
         }
-        //TODO: 3D - verify that depthBuffer is working correctly
-        //TODO: 3D - complete antiAliasing
+        if (antiAliasing && !com.sun.prism.GraphicsPipeline.
+                getPipeline().isAntiAliasingSupported()) {
+            String logname = SubScene.class.getName();
+            PlatformLogger.getLogger(logname).warning("System can't support "
+                    + "antiAliasing");
+        }
     }
 
+    private static boolean is3DSupported =
+            Platform.isSupported(ConditionalFeature.SCENE3D);
+
+    private final boolean antiAliasing;
+
     /**
      * Return true if this {@code SubScene} is anti-aliased otherwise false.
      */
-    public boolean isAntiAliasing() {
-        throw new UnsupportedOperationException("Unsupported --- *** isAntiAliasing method ***");
+    public final boolean isAntiAliasing() {
+        return antiAliasing;
     }
 
-    private boolean depthBuffer = false;
+    private final boolean depthBuffer;
 
-    boolean isDepthBufferInteral() {
-        if (!Platform.isSupported(ConditionalFeature.SCENE3D)) {
-            return false;
-        }
+    /**
+     * Retrieves the depth buffer attribute for this SubScene.
+     * @return the depth buffer attribute.
+     */
+    public final boolean isDepthBuffer() {
         return depthBuffer;
     }
+
+    private boolean isDepthBufferInternal() {
+        return is3DSupported ? depthBuffer : false;
+    }
+
     /**
      * Defines the root {@code Node} of the SubScene scene graph.
      * If a {@code Group} is used as the root, the
@@ -263,7 +271,7 @@
                     Camera _value = get();
                     if (_value != null) {
                         if (_value instanceof PerspectiveCamera
-                                && !Platform.isSupported(ConditionalFeature.SCENE3D)) {
+                                && !SubScene.is3DSupported) {
                             String logname = SubScene.class.getName();
                             PlatformLogger.getLogger(logname).warning("System can't support "
                                     + "ConditionalFeature.SCENE3D");
@@ -305,8 +313,7 @@
     Camera getEffectiveCamera() {
         final Camera cam = getCamera();
         if (cam == null
-                || (cam instanceof PerspectiveCamera
-                && !Platform.isSupported(ConditionalFeature.SCENE3D))) {
+                || (cam instanceof PerspectiveCamera && !is3DSupported)) {
             if (defaultCamera == null) {
                 defaultCamera = new ParallelCamera();
                 defaultCamera.setOwnerSubScene(this);
@@ -487,7 +494,6 @@
                 peer.setFillPaint(platformPaint);
                 contentChanged = true;
             }
-            peer.setDepthBuffer(isDepthBufferInteral());
             if (isDirty(SubSceneDirtyBits.SIZE_DIRTY)) {
                 // Note change in size is a geom change and is handled by peer
                 peer.setWidth((float)getWidth());
@@ -556,7 +562,11 @@
      */
     @Deprecated    @Override
     protected NGNode impl_createPeer() {
-        return new NGSubScene();
+        if (!is3DSupported) {
+            return new NGSubScene(false, false);
+        }
+        return new NGSubScene(depthBuffer, antiAliasing &&
+                com.sun.prism.GraphicsPipeline.getPipeline().isAntiAliasingSupported());
     }
 
     /**
@@ -769,7 +779,7 @@
 
             @Override
             public boolean isDepthBuffer(SubScene subScene) {
-                return subScene.isDepthBufferInteral();
+                return subScene.isDepthBufferInternal();
             };
 
             @Override
--- a/modules/graphics/src/main/native-prism-es2/GLContext.c	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/GLContext.c	Thu Jul 11 15:02:05 2013 -0700
@@ -35,31 +35,31 @@
 extern char *strJavaToC(JNIEnv *env, jstring str);
 
 void printGLError(GLenum errCode) {
-    fprintf(stderr, "*** GLError Code = ");
+    char const glCString[] = "*** GLError Code = ";
     switch (errCode) {
         case GL_NO_ERROR:
-            fprintf(stderr, "GL_NO_ERROR\n");
+            //fprintf(stderr, "%sGL_NO_ERROR\n", glCString);
             break;
         case GL_INVALID_ENUM:
-            fprintf(stderr, "GL_INVALID_ENUM\n");
+            fprintf(stderr, "%sGL_INVALID_ENUM\n", glCString);
             break;
         case GL_INVALID_VALUE:
-            fprintf(stderr, "GL_INVALID_VALUE\n");
+            fprintf(stderr, "%sGL_INVALID_VALUE\n", glCString);
             break;
         case GL_INVALID_OPERATION:
-            fprintf(stderr, "GL_INVALID_OPERATION\n");
+            fprintf(stderr, "%sGL_INVALID_OPERATION\n", glCString);
             break;
         case GL_STACK_OVERFLOW:
-            fprintf(stderr, "GL_STACK_OVERFLOW\n");
+            fprintf(stderr, "%sGL_STACK_OVERFLOW\n", glCString);
             break;
         case GL_STACK_UNDERFLOW:
-            fprintf(stderr, "GL_STACK_UNDERFLOW\n");
+            fprintf(stderr, "%sGL_STACK_UNDERFLOW\n", glCString);
             break;
         case GL_OUT_OF_MEMORY:
-            fprintf(stderr, "GL_OUT_OF_MEMORY\n");
+            fprintf(stderr, "%sGL_OUT_OF_MEMORY\n", glCString);
             break;
         default:
-            fprintf(stderr, "*** UNKNOWN ERROR CODE ***\n");
+            fprintf(stderr, "%s*** UNKNOWN ERROR CODE ***\n", glCString);
     }
 }
 
@@ -204,6 +204,14 @@
     }
 }
 
+void bindFBO(ContextInfo *ctxInfo, GLuint fboId) {
+    if ((ctxInfo == NULL) || (ctxInfo->glBindFramebuffer == NULL)) {
+        return;
+    }
+    ctxInfo->glBindFramebuffer(GL_FRAMEBUFFER, fboId);
+    ctxInfo->state.fbo = fboId;
+}
+
 /*
  * Class:     com_sun_prism_es2_GLContext
  * Method:    nActiveTexture
@@ -226,10 +234,7 @@
 JNIEXPORT void JNICALL Java_com_sun_prism_es2_GLContext_nBindFBO
 (JNIEnv *env, jclass class, jlong nativeCtxInfo, jint fboId) {
     ContextInfo *ctxInfo = (ContextInfo *) jlong_to_ptr(nativeCtxInfo);
-    if ((ctxInfo == NULL) || (ctxInfo->glBindFramebuffer == NULL)) {
-        return;
-    }
-    ctxInfo->glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) fboId);
+    bindFBO(ctxInfo, (GLuint)fboId);
 }
 
 /*
@@ -309,53 +314,165 @@
             clearColor, clearDepth, ignoreScissor);
 }
 
+int checkFramebufferStatus(ContextInfo *ctxInfo) {
+    GLenum status;
+    status = ctxInfo->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) {
+        switch(status) {
+            case GL_FRAMEBUFFER_COMPLETE:
+                return GL_FALSE;
+                break;
+            case GL_FRAMEBUFFER_UNSUPPORTED:
+            //Choose different formats
+                fprintf(stderr, "Framebuffer object format is unsupported by the video hardware. (GL_FRAMEBUFFER_UNSUPPORTED)(FBO - 820)\n");
+                break;
+            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+                fprintf(stderr, "Incomplete attachment. (GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)(FBO - 820)\n");
+                break;
+            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+                fprintf(stderr, "Incomplete missing attachment. (GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)(FBO - 820)\n");
+                break;
+            case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+                fprintf(stderr, "Incomplete dimensions. (GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT)(FBO - 820)\n");
+                break;
+            case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
+                fprintf(stderr, "Incomplete formats. (GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT)(FBO - 820)\n");
+                break;
+            case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+                fprintf(stderr, "Incomplete draw buffer. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)(FBO - 820)\n");
+                break; 
+            case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+                fprintf(stderr, "Incomplete read buffer. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)(FBO - 820)\n");
+                break;
+            case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
+                fprintf(stderr, "Incomplete multisample buffer. (GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)(FBO - 820)\n");
+                break;
+            default:
+                //Programming error; will fail on all hardware
+                fprintf(stderr, "Some video driver error or programming error occurred. Framebuffer object status is invalid. (FBO - 823)\n");
+                break;
+        }
+        return GL_TRUE;
+    }
+    return GL_FALSE;
+}
+
 /*
  * Class:     com_sun_prism_es2_GLContext
- * Method:    nCreateDepthBuffer
- * Signature: (JII)I
+ * Method:    nBlit
+ * Signature: (JIIIIIIIIII)I
  */
-JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nCreateDepthBuffer
-(JNIEnv *env, jclass class, jlong nativeCtxInfo, jint width, jint height) {
-    GLuint dbID;
-    GLenum status;
+JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nBlit
+(JNIEnv *env, jclass class, jlong nativeCtxInfo, jint srcFBO, jint dstFBO,
+            int jsrcX0, int jsrcY0, jint srcX1, jint srcY1,
+            int jdstX0, int jdstY0, jint dstX1, jint dstY1) {
     ContextInfo *ctxInfo = (ContextInfo *) jlong_to_ptr(nativeCtxInfo);
+    if ((ctxInfo == NULL) || (ctxInfo->glGenFramebuffers == NULL)
+            || (ctxInfo->glBindFramebuffer == NULL)
+            || (ctxInfo->glBlitFramebuffer == NULL)) {
+        return 0;
+    }
+
+    if (dstFBO == 0) {
+        dstFBO = ctxInfo->state.fbo;
+    }
+    //Bind the FBOs
+    ctxInfo->glBindFramebuffer(GL_READ_FRAMEBUFFER, (GLuint)srcFBO);
+    ctxInfo->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, (GLuint)dstFBO);
+    ctxInfo->glBlitFramebuffer(jsrcX0, jsrcY0, srcX1, srcY1,
+                               jdstX0, jdstY0, dstX1, dstY1,
+                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
+    /* TODO: iOS MSAA support:
+     * We are using glBlitFramebuffer to "resolve" the mutlisample buffer,
+     * to a color destination. iOS does things differently, it uses
+     * glResolveMultisampleFramebufferAPPLE() in place of glBlit...
+     * Problem is glResolve.. does not take arguments so we can't flip
+     * coordinate system.
+     */
+
+    // Restore previous FBO
+    ctxInfo->glBindFramebuffer(GL_FRAMEBUFFER, ctxInfo->state.fbo);
+    return ctxInfo->state.fbo;
+}
+
+GLuint attachRenderbuffer(ContextInfo *ctxInfo, GLuint rbID, GLenum attachment) {
+    //GLenum status;
+    ctxInfo->glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
+            GL_RENDERBUFFER, rbID);
+    ctxInfo->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+    //status = ctxInfo->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (checkFramebufferStatus(ctxInfo)) {
+        ctxInfo->glDeleteRenderbuffers(1, &rbID);
+        rbID = 0;
+        fprintf(stderr, "Error creating render buffer object %d\n", rbID);
+    } else {
+        // explicitly clear the render buffers, since it may contain
+        // garbage after initialization
+        clearBuffers(ctxInfo, 0, 0, 0, 0, JNI_FALSE, JNI_TRUE, JNI_TRUE);
+    }
+    return rbID;
+}
+
+GLuint createAndAttachRenderBuffer(ContextInfo *ctxInfo, GLsizei width, GLsizei height, GLsizei msaa, GLenum attachment) {
+    GLuint rbID;
+    GLenum internalFormat;
 
     if ((ctxInfo == NULL) || (ctxInfo->glGenRenderbuffers == NULL)
             || (ctxInfo->glBindRenderbuffer == NULL)
             || (ctxInfo->glRenderbufferStorage == NULL)
             || (ctxInfo->glFramebufferRenderbuffer == NULL)
+#ifndef IS_EGL
+            || (ctxInfo->glRenderbufferStorageMultisample == NULL)
+#endif
             || (ctxInfo->glCheckFramebufferStatus == NULL)
             || (ctxInfo->glDeleteRenderbuffers == NULL)) {
         return 0;
     }
 
+    if (attachment == GL_DEPTH_ATTACHMENT) {
+#ifdef IS_EGL
+        internalFormat = GL_DEPTH_COMPONENT16;
+#else
+        internalFormat = GL_DEPTH_COMPONENT;
+#endif
+    } else {
+        internalFormat = GL_RGBA8; //TODO verify format on RGBA or RGBA8
+    }
     // create a depth buffer
-    ctxInfo->glGenRenderbuffers(1, &dbID);
-    ctxInfo->glBindRenderbuffer(GL_RENDERBUFFER, dbID);
+    ctxInfo->glGenRenderbuffers(1, &rbID);
+    ctxInfo->glBindRenderbuffer(GL_RENDERBUFFER, rbID);
 #ifdef IS_EGL
-    ctxInfo->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
-            width, height);
+    ctxInfo->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height);
 #else
-    ctxInfo->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
-            width, height);
+    if (msaa) {
+        ctxInfo->glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, internalFormat, width, height);
+    } else {
+        ctxInfo->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height);
+    }
 #endif
-    ctxInfo->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-            GL_RENDERBUFFER, dbID);
-    ctxInfo->glBindRenderbuffer(GL_RENDERBUFFER, 0);
-    status = ctxInfo->glCheckFramebufferStatus(GL_FRAMEBUFFER);
-    if (status != GL_FRAMEBUFFER_COMPLETE) {
-        ctxInfo->glDeleteRenderbuffers(1, &dbID);
-        dbID = 0;
-        fprintf(stderr,
-                "Error creating depth buffer object with size =(%d, %d)",
-                (int) width, (int) height);
-    }
+    return attachRenderbuffer(ctxInfo, rbID, attachment);
+}
 
-    // explicitly clear the depth buffers, since it may contain
-    // garbage after initialization
-    clearBuffers(ctxInfo, 0, 0, 0, 0, JNI_FALSE, JNI_TRUE, JNI_TRUE);
+/*
+ * Class:     com_sun_prism_es2_GLContext
+ * Method:    nCreateDepthBuffer
+ * Signature: (JIII)I
+ */
+JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nCreateDepthBuffer
+(JNIEnv *env, jclass class, jlong nativeCtxInfo, jint width, jint height, int msaa) {
+    ContextInfo *ctxInfo = (ContextInfo *) jlong_to_ptr(nativeCtxInfo);
+    return createAndAttachRenderBuffer(ctxInfo, width, height, msaa, GL_DEPTH_ATTACHMENT);
+}
 
-    return dbID;
+/*
+ * Class:     com_sun_prism_es2_GLContext
+ * Method:    nCreateRenderBuffer
+ * Signature: (JIII)I
+ */
+JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nCreateRenderBuffer
+(JNIEnv *env, jclass class, jlong nativeCtxInfo, jint width, jint height, int msaa) {
+    ContextInfo *ctxInfo = (ContextInfo *) jlong_to_ptr(nativeCtxInfo);
+    return createAndAttachRenderBuffer(ctxInfo, width, height, msaa, GL_COLOR_ATTACHMENT0);
 }
 
 /*
@@ -365,47 +482,37 @@
  */
 JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nCreateFBO
 (JNIEnv *env, jclass class, jlong nativeCtxInfo, jint texID) {
-    GLint savedFboID;
     GLuint fboID;
-    GLenum status;
 
     ContextInfo *ctxInfo = (ContextInfo *) jlong_to_ptr(nativeCtxInfo);
     if ((ctxInfo == NULL) || (ctxInfo->glGenFramebuffers == NULL)
-            || (ctxInfo->glBindFramebuffer == NULL)
             || (ctxInfo->glFramebufferTexture2D == NULL)
             || (ctxInfo->glCheckFramebufferStatus == NULL)
             || (ctxInfo->glDeleteFramebuffers == NULL)) {
         return 0;
     }
 
-    // save current framebuffer object
-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) & savedFboID);
-
     // initialize framebuffer object
     ctxInfo->glGenFramebuffers(1, &fboID);
-    ctxInfo->glBindFramebuffer(GL_FRAMEBUFFER, fboID);
+    bindFBO(ctxInfo, fboID);
 
-    // attach color texture to framebuffer object
-    ctxInfo->glFramebufferTexture2D(GL_FRAMEBUFFER,
-            GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (GLuint) texID, 0);
-
-    status = ctxInfo->glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
-    // explicitly clear the color buffer, since it may contain garbage
-    // after initialization
-    clearBuffers(ctxInfo, 0, 0, 0, 0, JNI_TRUE, JNI_FALSE, JNI_TRUE);
-
-    // restore previous framebuffer objects
-    ctxInfo->glBindFramebuffer(GL_FRAMEBUFFER, savedFboID);
-
-    if (status != GL_FRAMEBUFFER_COMPLETE) {
-        ctxInfo->glDeleteFramebuffers(1, &fboID);
-        fboID = 0;
-        fprintf(stderr,
-                "Error creating framebuffer object with TexID %d)", (int) texID);
+    if (texID) {
+        // Attach color texture to framebuffer object
+        ctxInfo->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                        GL_TEXTURE_2D, (GLuint)texID, 0);
+        // Can't check status of FBO until after a buffer is attached to it
+        if (checkFramebufferStatus(ctxInfo)) {
+            ctxInfo->glDeleteFramebuffers(1, &fboID);
+            fprintf(stderr,
+                    "Error creating framebuffer object with TexID %d)\n", (int) texID);
+            return 0;
+        }
+        // explicitly clear the color buffer, since it may contain garbage
+        // after initialization
+        clearBuffers(ctxInfo, 0, 0, 0, 0, JNI_TRUE, JNI_FALSE, JNI_TRUE);
     }
 
-    return (jint) fboID;
+    return (jint)fboID;
 }
 
 /*
@@ -490,7 +597,8 @@
                 if (length) {
                     char* msg  =  (char *) malloc((length * sizeof(char)) + 1);
                     ctxInfo->glGetShaderInfoLog ( shaderProgram , length , NULL , msg );
-                    printf("Shader validation log: %s\n",msg);
+                    fprintf(stderr, "Shader validation log: %s\n", msg);
+                    fflush(stderr);
                     free(msg);
                 }
             }
@@ -613,6 +721,7 @@
     }
     return (jint) texID;
 }
+
 /*
  * Class:     com_sun_prism_es2_GLContext
  * Method:    nDisposeShaders
@@ -747,12 +856,25 @@
 JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nGetFBO
 (JNIEnv *env, jclass class) {
     GLint param;
+    /* TODO: Should use state.fbo vs Querying GL */
     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &param);
     return (jint) param;
 }
 
 /*
  * Class:     com_sun_prism_es2_GLContext
+ * Method:    nGetMaxSampleSize
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_sun_prism_es2_GLContext_nGetMaxSampleSize
+(JNIEnv *env, jclass class) {
+    GLint samples;
+    glGetIntegerv(GL_MAX_SAMPLES, &samples);
+    return (jint)samples;
+}
+
+/*
+ * Class:     com_sun_prism_es2_GLContext
  * Method:    nGetMaxTextureSize
  * Signature: ()I
  */
@@ -1104,6 +1226,27 @@
 
 /*
  * Class:     com_sun_prism_es2_GLContext
+ * Method:    nSetMSAA
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_sun_prism_es2_GLContext_nSetMSAA
+(JNIEnv *env, jclass class, jlong nativeCtxInfo, jboolean msaa) {
+#ifndef IS_EGL
+    ContextInfo *ctxInfo = (ContextInfo *) jlong_to_ptr(nativeCtxInfo);
+    if (ctxInfo == NULL) {
+        return;
+    }
+
+    if (msaa) {
+        glEnable(GL_MULTISAMPLE);
+    } else {
+        glDisable(GL_MULTISAMPLE);
+    }
+#endif
+}
+
+/*
+ * Class:     com_sun_prism_es2_GLContext
  * Method:    nSetDepthTest
  * Signature: (JZ)V
  */
--- a/modules/graphics/src/main/native-prism-es2/PrismES2Defs.h	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/PrismES2Defs.h	Thu Jul 11 15:02:05 2013 -0700
@@ -212,6 +212,9 @@
     GLboolean cullEnable;
     GLenum cullMode;
     GLenum fillMode;
+
+    /* Currently bound fbo */
+    GLuint fbo;
 };
 
 /* Typedef for context properties struct */
@@ -313,6 +316,9 @@
     PFNGLGENBUFFERSPROC glGenBuffers;
     PFNGLBINDBUFFERPROC glBindBuffer;
     PFNGLBUFFERDATAPROC glBufferData;
+    PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample;
+    PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
+    PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
 
     /* For state caching */
     StateInfo state;
--- a/modules/graphics/src/main/native-prism-es2/eglfb/eglUtils.c	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/eglfb/eglUtils.c	Thu Jul 11 15:02:05 2013 -0700
@@ -319,6 +319,12 @@
                               GET_DLSYM(handle, "glBufferSubData");
     ctxInfo->glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)
                                   GET_DLSYM(handle, "glGetShaderInfoLog");
+    ctxInfo->glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)
+                            GET_DLSYM(handle, "glTexImage2DMultisample");
+    ctxInfo->glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
+                            GET_DLSYM(handle, "glRenderbufferStorageMultisample");
+    ctxInfo->glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
+                            GET_DLSYM(handle, "glBlitFramebuffer");
 
     initState(ctxInfo);
     /* Releasing native resources */
--- a/modules/graphics/src/main/native-prism-es2/ios/IOSGLContext.c	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/ios/IOSGLContext.c	Thu Jul 11 15:02:05 2013 -0700
@@ -225,6 +225,12 @@
             getProcAddress("glBufferSubData");
     ctxInfo->glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)
             getProcAddress("glGetShaderInfoLog");
+    ctxInfo->glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)
+            getProcAddress("glTexImage2DMultisample");
+    ctxInfo->glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
+            getProcAddress("glRenderbufferStorageMultisample");
+    ctxInfo->glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
+            getProcAddress("glBlitFramebuffer");
 
     // initialize platform states and properties to match
     // cached states and properties
--- a/modules/graphics/src/main/native-prism-es2/macosx/MacGLContext.c	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/macosx/MacGLContext.c	Thu Jul 11 15:02:05 2013 -0700
@@ -246,6 +246,12 @@
             getProcAddress("glBufferSubData");
     ctxInfo->glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)
             getProcAddress("glGetShaderInfoLog");
+    ctxInfo->glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)
+            getProcAddress("glTexImage2DMultisample");
+    ctxInfo->glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
+            getProcAddress("glRenderbufferStorageMultisample");
+    ctxInfo->glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
+            getProcAddress("glBlitFramebuffer");
 
     // initialize platform states and properties to match
     // cached states and properties
--- a/modules/graphics/src/main/native-prism-es2/windows/WinGLContext.c	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/windows/WinGLContext.c	Thu Jul 11 15:02:05 2013 -0700
@@ -272,6 +272,12 @@
             wglGetProcAddress("glBufferSubData");
     ctxInfo->glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)
             wglGetProcAddress("glGetShaderInfoLog");
+    ctxInfo->glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)
+            wglGetProcAddress("glTexImage2DMultisample");
+    ctxInfo->glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
+            wglGetProcAddress("glRenderbufferStorageMultisample");
+    ctxInfo->glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
+            wglGetProcAddress("glBlitFramebuffer");
 
     if (isExtensionSupported(ctxInfo->wglExtensionStr,
             "WGL_EXT_swap_control")) {
--- a/modules/graphics/src/main/native-prism-es2/x11/X11GLContext.c	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/main/native-prism-es2/x11/X11GLContext.c	Thu Jul 11 15:02:05 2013 -0700
@@ -260,6 +260,12 @@
             dlsym(RTLD_DEFAULT, "glBufferSubData");
     ctxInfo->glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)
             dlsym(RTLD_DEFAULT, "glGetShaderInfoLog");
+    ctxInfo->glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)
+            dlsym(RTLD_DEFAULT,"glTexImage2DMultisample");
+    ctxInfo->glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
+            dlsym(RTLD_DEFAULT,"glRenderbufferStorageMultisample");
+    ctxInfo->glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
+            dlsym(RTLD_DEFAULT,"glBlitFramebuffer");
 
     if (isExtensionSupported(ctxInfo->glxExtensionStr,
             "GLX_SGI_swap_control")) {
--- a/modules/graphics/src/stub/java/com/sun/javafx/pgstub/StubStage.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/stub/java/com/sun/javafx/pgstub/StubStage.java	Thu Jul 11 15:02:05 2013 -0700
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public TKScene createTKScene(boolean depthBuffer) {
+    public TKScene createTKScene(boolean depthBuffer, boolean antiAliasing) {
         return new StubScene();
     }
 
--- a/modules/graphics/src/test/java/com/sun/javafx/sg/prism/TestGraphics.java	Thu Jul 11 11:10:00 2013 -0400
+++ b/modules/graphics/src/test/java/com/sun/javafx/sg/prism/TestGraphics.java	Thu Jul 11 15:02:05 2013 -0700
@@ -137,6 +137,9 @@
     public void setup3DRendering() {
     }
 
+    public void blit(RTTexture srcTex, RTTexture dstTex, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1) {
+    }
+
     private static class TestContext extends BaseContext {
 
         public TestContext() {
@@ -176,6 +179,9 @@
         @Override public int getRTTWidth(int w, WrapMode wrapMode) { return w; }
         @Override public int getRTTHeight(int h, WrapMode wrapMode) { return h; }
         @Override public RTTexture createRTTexture(final int width, final int height, Texture.WrapMode wrapMode) {
+            return createRTTexture(width, height, wrapMode, false);
+        }
+        @Override public RTTexture createRTTexture(final int width, final int height, Texture.WrapMode wrapMode, boolean antiAliasing) {
             return new RTTexture() {
                 @Override public int[] getPixels() { return new int[0]; }
                 @Override public boolean readPixels(Buffer pixels, int x, int y, int width, int height) { return false; }
@@ -217,6 +223,7 @@
                 @Override public void contentsUseful() { }
                 @Override public void contentsNotUseful() { }
                 @Override public void setOpaque(boolean opaque) { }
+                @Override public boolean isAntiAliasing() { return false; }
             };
         }
         @Override public Presentable createPresentable(PresentableState pstate) { return null; }
@@ -275,6 +282,10 @@
             return 0;
         }
 
+        public boolean isAntiAliasing() {
+            return false;
+        }
+
     }
 
     @Override