First draft of fbo in renderscript.
Updating samples and benchmark

Change-Id: I469bf8b842fca72b59475c8fa024c12cf0e14954
diff --git a/Android.mk b/Android.mk
index 14152d8..9100693 100644
--- a/Android.mk
+++ b/Android.mk
@@ -90,6 +90,7 @@
 	rsContext.cpp \
 	rsDevice.cpp \
 	rsElement.cpp \
+	rsFBOCache.cpp \
 	rsFileA3D.cpp \
 	rsFont.cpp \
 	rsLocklessFifo.cpp \
diff --git a/RenderScriptDefines.h b/RenderScriptDefines.h
index 4e1ac88..bb275b5 100644
--- a/RenderScriptDefines.h
+++ b/RenderScriptDefines.h
@@ -84,6 +84,7 @@
     RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE = 0x0002,
     RS_ALLOCATION_USAGE_GRAPHICS_VERTEX = 0x0004,
     RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS = 0x0008,
+    RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET = 0x0010,
 
     RS_ALLOCATION_USAGE_ALL = 0x000F
 };
@@ -147,6 +148,7 @@
     RS_KIND_PIXEL_LA,
     RS_KIND_PIXEL_RGB,
     RS_KIND_PIXEL_RGBA,
+    RS_KIND_PIXEL_DEPTH,
 };
 
 enum RsSamplerParam {
diff --git a/rsAllocation.cpp b/rsAllocation.cpp
index b8ddb0b..6b37e03 100644
--- a/rsAllocation.cpp
+++ b/rsAllocation.cpp
@@ -56,7 +56,8 @@
 
     mTextureID = 0;
     mBufferID = 0;
-    mUploadDefered = false;
+    mRenderTargetID = 0;
+    mUploadDeferred = false;
 
     mUserBitmapCallback = NULL;
     mUserBitmapCallbackData = NULL;
@@ -93,6 +94,10 @@
         glDeleteTextures(1, &mTextureID);
         mTextureID = 0;
     }
+    if (mRenderTargetID) {
+        glDeleteRenderbuffers(1, &mRenderTargetID);
+        mRenderTargetID = 0;
+    }
 #endif //ANDROID_RS_SERIALIZE
 }
 
@@ -112,9 +117,14 @@
     return false;
 }
 
-void Allocation::deferedUploadToTexture(const Context *rsc) {
+void Allocation::deferredUploadToTexture(const Context *rsc) {
     mHal.state.usageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE;
-    mUploadDefered = true;
+    mUploadDeferred = true;
+}
+
+void Allocation::deferredAllocateRenderTarget(const Context *rsc) {
+    mHal.state.usageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET;
+    mUploadDeferred = true;
 }
 
 uint32_t Allocation::getGLTarget() const {
@@ -155,8 +165,11 @@
     if (getIsBufferObject()) {
         uploadToBufferObject(rsc);
     }
+    if (getIsRenderTarget() && !getIsTexture()) {
+        allocateRenderTarget(rsc);
+    }
 
-    mUploadDefered = false;
+    mUploadDeferred = false;
 }
 
 void Allocation::uploadToTexture(const Context *rsc) {
@@ -184,7 +197,7 @@
             // Force a crash to 1: restart the app, 2: make sure we get a bugreport.
             LOGE("Upload to texture failed to gen mTextureID");
             rsc->dumpDebug();
-            mUploadDefered = true;
+            mUploadDeferred = true;
             return;
         }
         isFirstUpload = true;
@@ -200,6 +213,32 @@
 #endif //ANDROID_RS_SERIALIZE
 }
 
+void Allocation::allocateRenderTarget(const Context *rsc) {
+#ifndef ANDROID_RS_SERIALIZE
+    mHal.state.usageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET;
+
+    GLenum format = mHal.state.type->getElement()->getComponent().getGLFormat();
+    if (!format) {
+        return;
+    }
+
+    if (!mRenderTargetID) {
+        glGenRenderbuffers(1, &mRenderTargetID);
+
+        if (!mRenderTargetID) {
+            // This should generally not happen
+            LOGE("allocateRenderTarget failed to gen mRenderTargetID");
+            rsc->dumpDebug();
+            return;
+        }
+        glBindRenderbuffer(GL_RENDERBUFFER, mRenderTargetID);
+        glRenderbufferStorage(GL_RENDERBUFFER, format,
+                              mHal.state.type->getDimX(),
+                              mHal.state.type->getDimY());
+    }
+#endif //ANDROID_RS_SERIALIZE
+}
+
 #ifndef ANDROID_RS_SERIALIZE
 const static GLenum gFaceOrder[] = {
     GL_TEXTURE_CUBE_MAP_POSITIVE_X,
@@ -271,9 +310,9 @@
 #endif //ANDROID_RS_SERIALIZE
 }
 
-void Allocation::deferedUploadToBufferObject(const Context *rsc) {
+void Allocation::deferredUploadToBufferObject(const Context *rsc) {
     mHal.state.usageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_VERTEX;
-    mUploadDefered = true;
+    mUploadDeferred = true;
 }
 
 void Allocation::uploadToBufferObject(const Context *rsc) {
@@ -288,7 +327,7 @@
     }
     if (!mBufferID) {
         LOGE("Upload to buffer object failed");
-        mUploadDefered = true;
+        mUploadDeferred = true;
         return;
     }
     GLenum target = (GLenum)getGLTarget();
@@ -300,7 +339,7 @@
 }
 
 void Allocation::uploadCheck(Context *rsc) {
-    if (mUploadDefered) {
+    if (mUploadDeferred) {
         syncAll(rsc, RS_ALLOCATION_USAGE_SCRIPT);
     }
 }
@@ -329,7 +368,7 @@
 
     memcpy(ptr, data, size);
     sendDirty();
-    mUploadDefered = true;
+    mUploadDeferred = true;
 }
 
 void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face,
@@ -362,7 +401,7 @@
             dst += destW * eSize;
         }
         sendDirty();
-        mUploadDefered = true;
+        mUploadDeferred = true;
     } else {
         update2DTexture(data, xoff, yoff, lod, face, w, h);
     }
@@ -407,7 +446,7 @@
 
     memcpy(ptr, data, sizeBytes);
     sendDirty();
-    mUploadDefered = true;
+    mUploadDeferred = true;
 }
 
 void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y,
@@ -450,7 +489,7 @@
 
     memcpy(ptr, data, sizeBytes);
     sendDirty();
-    mUploadDefered = true;
+    mUploadDeferred = true;
 }
 
 void Allocation::addProgramToDirty(const Program *p) {
@@ -617,12 +656,12 @@
 
 void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, bool genmip, uint32_t baseMipLevel) {
     Allocation *alloc = static_cast<Allocation *>(va);
-    alloc->deferedUploadToTexture(rsc);
+    alloc->deferredUploadToTexture(rsc);
 }
 
 void rsi_AllocationUploadToBufferObject(Context *rsc, RsAllocation va) {
     Allocation *alloc = static_cast<Allocation *>(va);
-    alloc->deferedUploadToBufferObject(rsc);
+    alloc->deferredUploadToBufferObject(rsc);
 }
 
 static void mip565(const Adapter2D &out, const Adapter2D &in) {
@@ -792,7 +831,6 @@
     return alloc;
 }
 
-
 RsAllocation rsaAllocationCreateFromBitmap(RsContext con, RsType vtype,
                                            RsAllocationMipmapControl mips,
                                            const void *data, uint32_t usages) {
@@ -811,7 +849,7 @@
         rsaAllocationGenerateScriptMips(rsc, texAlloc);
     }
 
-    texAlloc->deferedUploadToTexture(rsc);
+    texAlloc->deferredUploadToTexture(rsc);
     return texAlloc;
 }
 
@@ -852,7 +890,7 @@
         rsaAllocationGenerateScriptMips(rsc, texAlloc);
     }
 
-    texAlloc->deferedUploadToTexture(rsc);
+    texAlloc->deferredUploadToTexture(rsc);
     return texAlloc;
 }
 
diff --git a/rsAllocation.h b/rsAllocation.h
index e63140c..d334841 100644
--- a/rsAllocation.h
+++ b/rsAllocation.h
@@ -71,13 +71,17 @@
 
     void syncAll(Context *rsc, RsAllocationUsageType src);
 
-    void deferedUploadToTexture(const Context *rsc);
+    void deferredUploadToTexture(const Context *rsc);
     void uploadToTexture(const Context *rsc);
     uint32_t getTextureID() const {return mTextureID;}
 
+    void deferredAllocateRenderTarget(const Context *rsc);
+    void allocateRenderTarget(const Context *rsc);
+    uint32_t getRenderTargetID() const {return mRenderTargetID;}
+
     uint32_t getGLTarget() const;
 
-    void deferedUploadToBufferObject(const Context *rsc);
+    void deferredUploadToBufferObject(const Context *rsc);
     void uploadToBufferObject(const Context *rsc);
     uint32_t getBufferObjectID() const {return mBufferID;}
 
@@ -118,6 +122,9 @@
     bool getIsTexture() const {
         return (mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE) != 0;
     }
+    bool getIsRenderTarget() const {
+        return (mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET) != 0;
+    }
     bool getIsBufferObject() const {
         return (mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_VERTEX) != 0;
     }
@@ -161,7 +168,10 @@
     // is allowed.
     uint32_t mBufferID;
 
-    bool mUploadDefered;
+    // Is this a legal structure to be used as an FBO render target
+    uint32_t mRenderTargetID;
+
+    bool mUploadDeferred;
 
 private:
     void init(Context *rsc, const Type *);
diff --git a/rsComponent.cpp b/rsComponent.cpp
index 4c4987a..e2ae043 100644
--- a/rsComponent.cpp
+++ b/rsComponent.cpp
@@ -18,6 +18,7 @@
 
 #ifndef ANDROID_RS_SERIALIZE
 #include <GLES/gl.h>
+#include <GLES2/gl2.h>
 #endif
 
 using namespace android;
@@ -207,6 +208,7 @@
     case RS_KIND_PIXEL_LA: return GL_LUMINANCE_ALPHA;
     case RS_KIND_PIXEL_RGB: return GL_RGB;
     case RS_KIND_PIXEL_RGBA: return GL_RGBA;
+    case RS_KIND_PIXEL_DEPTH: return GL_DEPTH_COMPONENT16;
     default: break;
     }
 #endif //ANDROID_RS_SERIALIZE
diff --git a/rsContext.cpp b/rsContext.cpp
index c761c75..d727ba1 100644
--- a/rsContext.cpp
+++ b/rsContext.cpp
@@ -409,6 +409,7 @@
     mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
     mRaster->setupGL2(this, &mStateRaster);
     mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
+    mFBOCache.setupGL2(this);
     return true;
 }
 
diff --git a/rsContext.h b/rsContext.h
index 72574a6..eacfdf7 100644
--- a/rsContext.h
+++ b/rsContext.h
@@ -38,6 +38,7 @@
 #include "rsProgramRaster.h"
 #include "rsProgramVertex.h"
 #include "rsShaderCache.h"
+#include "rsFBOCache.h"
 #include "rsVertexArray.h"
 
 #include "rsgApiStructs.h"
@@ -119,6 +120,7 @@
 
     ScriptCState mScriptC;
     ShaderCache mShaderCache;
+    FBOCache mFBOCache;
 
     void swapBuffers();
     void setRootScript(Script *);
diff --git a/rsFBOCache.cpp b/rsFBOCache.cpp
new file mode 100644
index 0000000..78aa8ce
--- /dev/null
+++ b/rsFBOCache.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsFBOCache.h"
+
+#include "rsContext.h"
+#include "rsAllocation.h"
+
+#ifndef ANDROID_RS_SERIALIZE
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#endif //ANDROID_RS_SERIALIZE
+
+using namespace android;
+using namespace android::renderscript;
+
+
+FBOCache::FBOCache() {
+    mFBOId = 0;
+    mDirty = false;
+    mMaxTargets = 1;
+    mColorTargets = new ObjectBaseRef<Allocation>[mMaxTargets];
+}
+
+FBOCache::~FBOCache() {
+    delete[] mColorTargets;
+#ifndef ANDROID_RS_SERIALIZE
+    if(mFBOId != 0) {
+        glDeleteFramebuffers(1, &mFBOId);
+    }
+#endif //ANDROID_RS_SERIALIZE
+}
+
+void FBOCache::bindColorTarget(Context *rsc, Allocation *a, uint32_t slot) {
+    if (slot >= mMaxTargets) {
+        LOGE("Invalid render target index");
+        return;
+    }
+    if (a != NULL) {
+        if (!a->getIsTexture()) {
+            LOGE("Invalid Color Target");
+            return;
+        }
+        if (a->getIsTexture()) {
+            if (a->getTextureID() == 0) {
+                a->deferredUploadToTexture(rsc);
+            }
+        } else if (a->getRenderTargetID() == 0) {
+            a->deferredAllocateRenderTarget(rsc);
+        }
+    }
+    mColorTargets[slot].set(a);
+    mDirty = true;
+}
+
+void FBOCache::bindDepthTarget(Context *rsc, Allocation *a) {
+    if (a != NULL) {
+        if (!a->getIsRenderTarget()) {
+            LOGE("Invalid Depth Target");
+            return;
+        }
+        if (a->getIsTexture()) {
+            if (a->getTextureID() == 0) {
+                a->deferredUploadToTexture(rsc);
+            }
+        } else if (a->getRenderTargetID() == 0) {
+            a->deferredAllocateRenderTarget(rsc);
+        }
+    }
+    mDepthTarget.set(a);
+    mDirty = true;
+}
+
+void FBOCache::resetAll(Context *) {
+    for (uint32_t i = 0; i < mMaxTargets; i ++) {
+        mColorTargets[i].set(NULL);
+    }
+    mDepthTarget.set(NULL);
+    mDirty = true;
+}
+
+bool FBOCache::renderToFramebuffer() {
+    if (mDepthTarget.get() != NULL) {
+        return false;
+    }
+
+    for (uint32_t i = 0; i < mMaxTargets; i ++) {
+        if (mColorTargets[i].get() != NULL) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void FBOCache::checkError(Context *rsc) {
+    GLenum status;
+    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    switch (status) {
+    case GL_FRAMEBUFFER_COMPLETE:
+        break;
+    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+        rsc->setError(RS_ERROR_BAD_VALUE,
+                      "Unable to set up render Target: RFRAMEBUFFER_INCOMPLETE_ATTACHMENT");
+        break;
+    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+        rsc->setError(RS_ERROR_BAD_VALUE,
+                      "Unable to set up render Target: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
+        break;
+    case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+        rsc->setError(RS_ERROR_BAD_VALUE,
+                      "Unable to set up render Target: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
+        break;
+    case GL_FRAMEBUFFER_UNSUPPORTED:
+        rsc->setError(RS_ERROR_BAD_VALUE,
+                      "Unable to set up render Target: GL_FRAMEBUFFER_UNSUPPORTED");
+        break;
+    }
+}
+
+void FBOCache::setDepthAttachment(Context *rsc) {
+#ifndef ANDROID_RS_SERIALIZE
+    if (mDepthTarget.get() != NULL) {
+        mDepthTarget->uploadCheck(rsc);
+        if (mDepthTarget->getIsTexture()) {
+            uint32_t texID = mDepthTarget->getTextureID();
+            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                                   GL_TEXTURE_2D, texID, 0);
+        } else {
+            uint32_t texID = mDepthTarget->getRenderTargetID();
+            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                                      GL_RENDERBUFFER, texID);
+        }
+    } else {
+        // Reset last attachment
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                                  GL_RENDERBUFFER, 0);
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                               GL_TEXTURE_2D, 0, 0);
+    }
+#endif //ANDROID_RS_SERIALIZE
+}
+
+void FBOCache::setColorAttachment(Context *rsc) {
+#ifndef ANDROID_RS_SERIALIZE
+    // Now attach color targets
+    for (uint32_t i = 0; i < mMaxTargets; i ++) {
+        uint32_t texID = 0;
+        if (mColorTargets[i].get() != NULL) {
+            mColorTargets[i]->uploadCheck(rsc);
+            if (mColorTargets[i]->getIsTexture()) {
+                uint32_t texID = mColorTargets[i]->getTextureID();
+                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
+                                       GL_TEXTURE_2D, texID, 0);
+            } else {
+                uint32_t texID = mDepthTarget->getRenderTargetID();
+                glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
+                                          GL_RENDERBUFFER, texID);
+            }
+        } else {
+            // Reset last attachment
+            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
+                                      GL_RENDERBUFFER, 0);
+            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
+                                   GL_TEXTURE_2D, 0, 0);
+        }
+    }
+#endif //ANDROID_RS_SERIALIZE
+}
+
+void FBOCache::setupGL2(Context *rsc) {
+#ifndef ANDROID_RS_SERIALIZE
+    if (!mDirty) {
+        return;
+    }
+
+    bool framebuffer = renderToFramebuffer();
+
+    if (!framebuffer) {
+        if(mFBOId == 0) {
+            glGenFramebuffers(1, &mFBOId);
+        }
+        glBindFramebuffer(GL_FRAMEBUFFER, mFBOId);
+
+        setDepthAttachment(rsc);
+        setColorAttachment(rsc);
+
+        glViewport(0, 0, mColorTargets[0]->getType()->getDimX(),
+                         mColorTargets[0]->getType()->getDimY());
+
+        checkError(rsc);
+    } else {
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+        glViewport(0, 0, rsc->getWidth(), rsc->getHeight());
+    }
+#endif //ANDROID_RS_SERIALIZE
+}
diff --git a/rsFBOCache.h b/rsFBOCache.h
new file mode 100644
index 0000000..9a0a3b6
--- /dev/null
+++ b/rsFBOCache.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAME_BUFFER_OBJECT_CACHE_H
+#define ANDROID_FRAME_BUFFER_OBJECT_CACHE_H
+
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Allocation;
+
+class FBOCache {
+public:
+    FBOCache();
+    ~FBOCache();
+
+    void bindColorTarget(Context *rsc, Allocation *a, uint32_t slot);
+    void bindDepthTarget(Context *, Allocation *a);
+    void resetAll(Context *);
+
+    void setupGL2(Context *);
+
+protected:
+
+    bool mDirty;
+    uint32_t mMaxTargets;
+    void checkError(Context *);
+    void setColorAttachment(Context *rsc);
+    void setDepthAttachment(Context *rsc);
+    bool renderToFramebuffer();
+    ObjectBaseRef<Allocation> *mColorTargets;
+    ObjectBaseRef<Allocation> mDepthTarget;
+
+    uint32_t mFBOId;
+
+};
+
+} // renderscript
+} // android
+
+#endif //ANDROID_FRAME_BUFFER_OBJECT_CACHE_H
diff --git a/rsFont.cpp b/rsFont.cpp
index 01dbab8..595c89a 100644
--- a/rsFont.cpp
+++ b/rsFont.cpp
@@ -566,7 +566,7 @@
         indexPtr[i6 + 5] = i4 + 3;
     }
 
-    indexAlloc->deferedUploadToBufferObject(mRSC);
+    indexAlloc->deferredUploadToBufferObject(mRSC);
     mIndexBuffer.set(indexAlloc);
 
     const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
diff --git a/rsMesh.cpp b/rsMesh.cpp
index 76fe62d..e29c800 100644
--- a/rsMesh.cpp
+++ b/rsMesh.cpp
@@ -282,13 +282,13 @@
 void Mesh::uploadAll(Context *rsc) {
     for (uint32_t ct = 0; ct < mVertexBufferCount; ct ++) {
         if (mVertexBuffers[ct].get()) {
-            mVertexBuffers[ct]->deferedUploadToBufferObject(rsc);
+            mVertexBuffers[ct]->deferredUploadToBufferObject(rsc);
         }
     }
 
     for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) {
         if (mPrimitives[ct]->mIndexBuffer.get()) {
-            mPrimitives[ct]->mIndexBuffer->deferedUploadToBufferObject(rsc);
+            mPrimitives[ct]->mIndexBuffer->deferredUploadToBufferObject(rsc);
         }
     }
 }
diff --git a/rsScriptC_LibGL.cpp b/rsScriptC_LibGL.cpp
index 4047049..1ed0f31 100644
--- a/rsScriptC_LibGL.cpp
+++ b/rsScriptC_LibGL.cpp
@@ -86,6 +86,33 @@
     rsi_ContextBindProgramRaster(rsc, pv);
 }
 
+static void SC_bindFrameBufferObjectColorTarget(RsAllocation va, uint32_t slot) {
+    CHECK_OBJ(va);
+    GET_TLS();
+    rsc->mFBOCache.bindColorTarget(rsc, static_cast<Allocation *>(va), slot);
+}
+
+static void SC_bindFrameBufferObjectDepthTarget(RsAllocation va) {
+    CHECK_OBJ(va);
+    GET_TLS();
+    rsc->mFBOCache.bindDepthTarget(rsc, static_cast<Allocation *>(va));
+}
+
+static void SC_clearFrameBufferObjectColorTarget(uint32_t slot) {
+    GET_TLS();
+    rsc->mFBOCache.bindColorTarget(rsc, NULL, slot);
+}
+
+static void SC_clearFrameBufferObjectDepthTarget() {
+    GET_TLS();
+    rsc->mFBOCache.bindDepthTarget(rsc, NULL);
+}
+
+static void SC_clearFrameBufferObjectTargets() {
+    GET_TLS();
+    rsc->mFBOCache.resetAll(rsc);
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // VP
 //////////////////////////////////////////////////////////////////////////////
@@ -275,6 +302,10 @@
     pf->setConstantColor(rsc, r, g, b, a);
 }
 
+static void SC_finish() {
+    glFinish();
+}
+
 static void SC_allocationSyncAll(RsAllocation va) {
     CHECK_OBJ(va);
     GET_TLS();
@@ -291,6 +322,7 @@
 
 static void SC_ClearColor(float r, float g, float b, float a) {
     GET_TLS();
+    rsc->mFBOCache.setupGL2(rsc);
     rsc->setupProgramStore();
 
     glClearColor(r, g, b, a);
@@ -299,6 +331,7 @@
 
 static void SC_ClearDepth(float v) {
     GET_TLS();
+    rsc->mFBOCache.setupGL2(rsc);
     rsc->setupProgramStore();
 
     glClearDepthf(v);
@@ -444,8 +477,15 @@
     { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont, false },
     { "_Z12rsgFontColorffff", (void *)&SC_FontColor, false },
 
+    { "_Z18rsgBindColorTarget13rs_allocationj", (void *)&SC_bindFrameBufferObjectColorTarget, false },
+    { "_Z18rsgBindDepthTarget13rs_allocation", (void *)&SC_bindFrameBufferObjectDepthTarget, false },
+    { "_Z19rsgClearColorTargetj", (void *)&SC_clearFrameBufferObjectColorTarget, false },
+    { "_Z19rsgClearDepthTargetv", (void *)&SC_clearFrameBufferObjectDepthTarget, false },
+    { "_Z24rsgClearAllRenderTargetsv", (void *)&SC_clearFrameBufferObjectTargets, false },
+
     // misc
     { "_Z5colorffff", (void *)&SC_color, false },
+    { "_Z9rsgFinishv", (void *)&SC_finish, false },
 
     { NULL, NULL, false }
 };
diff --git a/scriptc/rs_graphics.rsh b/scriptc/rs_graphics.rsh
index 67ffc3d..d53bc95 100644
--- a/scriptc/rs_graphics.rsh
+++ b/scriptc/rs_graphics.rsh
@@ -1,6 +1,46 @@
 #ifndef __RS_GRAPHICS_RSH__
 #define __RS_GRAPHICS_RSH__
 
+/**
+ * Set the color target used for all subsequent rendering calls
+ * @param colorTarget
+ * @param slot
+ */
+extern void __attribute__((overloadable))
+    rsgBindColorTarget(rs_allocation colorTarget, uint slot);
+
+/**
+ * Clear the previously set color target
+ * @param slot
+ */
+extern void __attribute__((overloadable))
+    rsgClearColorTarget(uint slot);
+
+/**
+ * Set the depth target used for all subsequent rendering calls
+ * @param depthTarget
+ */
+extern void __attribute__((overloadable))
+    rsgBindDepthTarget(rs_allocation depthTarget);
+
+/**
+ * Clear the previously set depth target
+ */
+extern void __attribute__((overloadable))
+    rsgClearDepthTarget(void);
+
+/**
+ * Clear all color and depth targets and resume rendering into
+ * the framebuffer
+ */
+extern void __attribute__((overloadable))
+    rsgClearAllRenderTargets(void);
+
+/**
+ * Force RenderScript to finish all rendering commands
+ */
+extern uint __attribute__((overloadable))
+    rsgFinish(void);
 
 /**
  * Bind a new ProgramFragment to the rendering context.