created an new EGL extension called ANDROID_swap_rectangle

ANDROID_swap_rectangle allows to specify the rectangle affected by eglSwapBuffers(), anything outside of this rectangle is unchanged. in particular EGL_BUFFER_DESTROYED only applies to that rectangle. This extension as well as EGL_BUFFER_PRESERVED allow major optimizations on surfaceflinger, which can redraw only the dirty area during compositing.

However, ANDROID_swap_rectangle allows further optimizations in EGL by reducing the amount of copy-back needed. ANDROID_swap_rectangle is particularily important for software implementations.
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 374f2e2..fc29d73 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -195,17 +195,19 @@
      * Create our main surface
      */
 
-    
     surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
     checkEGLErrors("eglCreateDisplaySurfaceANDROID");
 
-    
     if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) {
         if (dummy == EGL_BUFFER_PRESERVED) {
             mFlags |= BUFFER_PRESERVED;
         }
     }
-    
+
+    if (strstr(egl_extensions, "ANDROID_swap_rectangle")) {
+        mFlags |= SWAP_RECTANGLE;
+    }
+
     mDpiX = mNativeWindow->xdpi;
     mDpiX = mNativeWindow->ydpi;
     mRefreshRate = mNativeWindow->getDevice()->fps; 
@@ -304,11 +306,12 @@
     EGLDisplay dpy = mDisplay;
     EGLSurface surface = mSurface;
 
-    if (mFlags & BUFFER_PRESERVED) {
+    if (mFlags & SWAP_RECTANGLE) {
         Region newDirty(dirty);
         newDirty.andSelf(Rect(mWidth, mHeight));
         const Rect& b(newDirty.bounds());
-        //mNativeWindow->setSwapRectangle(b);
+        eglSetSwapRectangleANDROID(dpy, surface,
+                b.left, b.top, b.width(), b.height());
     } 
 
     mPageFlipCount++;
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
index c9c75e2..c3dbff1 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -50,6 +50,7 @@
         BUFFER_PRESERVED        = 0x00010000,
         UPDATE_ON_DEMAND        = 0x00020000,   // video driver feature
         SLOW_CONFIG             = 0x00040000,   // software
+        SWAP_RECTANGLE          = 0x00080000,
     };
 
     DisplayHardware(
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 5fd979e..b8c246c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -803,12 +803,14 @@
     mInvalidRegion.orSelf(mDirtyRegion);
 
     uint32_t flags = hw.getFlags();
-    if (flags & DisplayHardware::BUFFER_PRESERVED) {
-        // here we assume DisplayHardware::flip()'s  implementation
-        // performs the copy-back optimization.
+    if ((flags & DisplayHardware::SWAP_RECTANGLE) || 
+        (flags & DisplayHardware::BUFFER_PRESERVED)) 
+    {
+        // we can redraw only what's dirty
     } else {
         if (flags & DisplayHardware::UPDATE_ON_DEMAND) {
-            // we need to fully redraw the part that will be updated
+            // we need to redraw the rectangle that will be updated
+            // (pushed to the framebuffer).
             mDirtyRegion.set(mInvalidRegion.bounds());
         } else {
             // we need to redraw everything
@@ -890,7 +892,9 @@
 {
      const DisplayHardware& hw(graphicPlane(0).displayHardware());
      const uint32_t flags = hw.getFlags();
-     if (!(flags & DisplayHardware::BUFFER_PRESERVED)) {
+
+     if (!((flags & DisplayHardware::SWAP_RECTANGLE) ||
+             (flags & DisplayHardware::BUFFER_PRESERVED))) {
          const Region repaint((flags & DisplayHardware::UPDATE_ON_DEMAND) ?
                  mDirtyRegion.bounds() : hw.bounds());
          composeSurfaces(repaint);
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 25cfcb8..335b0b0 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -131,6 +131,18 @@
 /* Interfaces defined by EGL_KHR_image above */
 #endif
 
+
+#ifndef EGL_ANDROID_swap_rectangle
+#define EGL_ANDROID_swap_rectangle 1
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSetSwapRectangleANDROID (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height);
+#endif /* EGL_EGLEXT_PROTOTYPES */
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSWAPRECTANGLEANDROIDPROC) (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height);
+#endif
+
+
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 1a774f5..4cf0bf8 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -157,6 +157,7 @@
     virtual     EGLint      getRefreshRate() const;
     virtual     EGLint      getSwapBehavior() const;
     virtual     EGLBoolean  swapBuffers();
+    virtual     EGLBoolean  setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
 protected:
     GGLSurface              depth;
 };
@@ -190,6 +191,11 @@
 EGLint egl_surface_t::getSwapBehavior() const {
     return EGL_BUFFER_PRESERVED;
 }
+EGLBoolean egl_surface_t::setSwapRectangle(
+        EGLint l, EGLint t, EGLint w, EGLint h)
+{
+    return EGL_FALSE;
+}
 
 // ----------------------------------------------------------------------------
 
@@ -214,16 +220,104 @@
     virtual     EGLint      getVerticalResolution() const;
     virtual     EGLint      getRefreshRate() const;
     virtual     EGLint      getSwapBehavior() const;
+    virtual     EGLBoolean  setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
 
 private:
     status_t lock(android_native_buffer_t* buf, int usage, void** vaddr);
     status_t unlock(android_native_buffer_t* buf);
     android_native_window_t*   nativeWindow;
     android_native_buffer_t*   buffer;
+    android_native_buffer_t*   previousBuffer;
     gralloc_module_t const*    module;
     int width;
     int height;
     void* bits;
+    GGLFormat const* pixelFormatTable;
+    
+    struct Rect {
+        inline Rect() { };
+        inline Rect(int32_t w, int32_t h)
+            : left(0), top(0), right(w), bottom(h) { }
+        inline Rect(int32_t l, int32_t t, int32_t r, int32_t b)
+            : left(l), top(t), right(r), bottom(b) { }
+        Rect& andSelf(const Rect& r) {
+            left   = max(left, r.left);
+            top    = max(top, r.top);
+            right  = min(right, r.right);
+            bottom = min(bottom, r.bottom);
+            return *this;
+        }
+        bool isEmpty() const {
+            return (left>=right || top>=bottom);
+        }
+        void dump(char const* what) {
+            LOGD("%s { %5d, %5d, w=%5d, h=%5d }", 
+                    what, left, top, right-left, bottom-top);
+        }
+        
+        int32_t left;
+        int32_t top;
+        int32_t right;
+        int32_t bottom;
+    };
+
+    struct Region {
+        inline Region() : count(0) { }
+        static Region subtract(const Rect& lhs, const Rect& rhs) {
+            Region reg;
+            Rect* storage = reg.storage;
+            if (!lhs.isEmpty()) {
+                if (lhs.top < rhs.top) { // top rect
+                    storage->left   = lhs.left;
+                    storage->top    = lhs.top;
+                    storage->right  = lhs.right;
+                    storage->bottom = max(lhs.top, rhs.top);
+                    storage++;
+                }
+                if (lhs.left < rhs.left) { // left-side rect
+                    storage->left   = lhs.left;
+                    storage->top    = max(lhs.top, rhs.top);
+                    storage->right  = max(lhs.left, rhs.left);
+                    storage->bottom = min(lhs.bottom, rhs.bottom);
+                    storage++;
+                }
+                if (lhs.right > rhs.right) { // right-side rect
+                    storage->left   = min(lhs.right, rhs.right);
+                    storage->top    = max(lhs.top, rhs.top);
+                    storage->right  = lhs.right;
+                    storage->bottom = min(lhs.bottom, rhs.bottom);
+                    storage++;
+                }
+                if (lhs.bottom > rhs.bottom) { // bottom rect
+                    storage->left   = lhs.left;
+                    storage->top    = min(lhs.bottom, rhs.bottom);
+                    storage->right  = lhs.right;
+                    storage->bottom = lhs.bottom;
+                    storage++;
+                }
+                reg.count = storage - reg.storage;
+            }
+            return reg;
+        }
+        bool isEmpty() const {
+            return count<=0;
+        }
+        ssize_t getRects(Rect const* * rects) const {
+            *rects = storage;
+            return count;
+        }
+    private:
+        Rect storage[4];
+        ssize_t count;
+    };
+    
+    void copyBlt(
+            android_native_buffer_t* dst, void* dst_vaddr,
+            android_native_buffer_t* src, void const* src_vaddr,
+            const Rect* reg, ssize_t count);
+
+    Rect dirtyRegion;
+    Rect oldDirtyRegion;
 };
 
 egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
@@ -231,16 +325,22 @@
         int32_t depthFormat,
         android_native_window_t* window)
     : egl_surface_t(dpy, config, depthFormat), 
-    nativeWindow(window), buffer(0), module(0), bits(NULL)
+    nativeWindow(window), buffer(0), previousBuffer(0), module(0),
+    bits(NULL)
 {
     hw_module_t const* pModule;
     hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
     module = reinterpret_cast<gralloc_module_t const*>(pModule);
 
+    pixelFormatTable = gglGetPixelFormatTable();
+    
+    // keep a reference on the window
     nativeWindow->common.incRef(&nativeWindow->common);
 
+    // dequeue a buffer
     nativeWindow->dequeueBuffer(nativeWindow, &buffer);
-    
+
+    // allocate a corresponding depth-buffer
     width = buffer->width;
     height = buffer->height;
     if (depthFormat) {
@@ -254,14 +354,32 @@
         }
     }
 
+    // keep a reference on the buffer
     buffer->common.incRef(&buffer->common);
 }
 
+egl_window_surface_v2_t::~egl_window_surface_v2_t() {
+    if (buffer) {
+        buffer->common.decRef(&buffer->common);
+    }
+    if (previousBuffer) {
+        previousBuffer->common.decRef(&previousBuffer->common); 
+    }
+    nativeWindow->common.decRef(&nativeWindow->common);
+}
+
 void egl_window_surface_v2_t::connect() 
 {
     // Lock the buffer
     nativeWindow->lockBuffer(nativeWindow, buffer);
-    lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, &bits);
+    // pin the buffer down
+    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
+            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+        LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+                buffer, buffer->width, buffer->height);
+        setError(EGL_BAD_ACCESS, EGL_NO_SURFACE);
+        // FIXME: we should make sure we're not accessing the buffer anymore
+    }
 }
 
 void egl_window_surface_v2_t::disconnect() 
@@ -286,38 +404,86 @@
     return err;
 }
 
+void egl_window_surface_v2_t::copyBlt(
+        android_native_buffer_t* dst, void* dst_vaddr,
+        android_native_buffer_t* src, void const* src_vaddr,
+        const Rect* reg, ssize_t count)
+{
+    // FIXME: use copybit if possible
+    // NOTE: dst and src must be the same format
+    
+    Rect r;
+    const size_t bpp = pixelFormatTable[src->format].size;
+    const size_t dbpr = dst->stride * bpp;
+    const size_t sbpr = src->stride * bpp;
 
-egl_window_surface_v2_t::~egl_window_surface_v2_t() {
-    if (buffer) {
-        buffer->common.decRef(&buffer->common);
+    uint8_t const * const src_bits = (uint8_t const *)src_vaddr;
+    uint8_t       * const dst_bits = (uint8_t       *)dst_vaddr;
+    
+    for (int i= 0 ; i<count ; i++) {
+        const Rect& r(reg[i]);
+        ssize_t w = r.right - r.left;
+        ssize_t h = r.bottom - r.top;
+        if (w <= 0 || h<=0) continue;
+        size_t size = w * bpp;
+        uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
+        uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
+        if (dbpr==sbpr && size==sbpr) {
+            size *= h;
+            h = 1;
+        }
+        do {
+            memcpy(d, s, size);
+            d += dbpr;
+            s += sbpr;
+        } while (--h > 0);
     }
-    nativeWindow->common.decRef(&nativeWindow->common);
 }
 
 EGLBoolean egl_window_surface_v2_t::swapBuffers()
 {
-    // TODO: this is roughly the code needed for preserving the back buffer
-    // efficiently. dirty is the area that has been modified.
-    //Region newDirty(dirty);
-    //newDirty.andSelf(Rect(nativeWindow->width, nativeWindow->height));
-    //mDirty = newDirty;
-    //const Region copyback(mDirty.subtract(newDirty));
-    //mDisplaySurface->copyFrontToBack(copyback);
+    /*
+     * Handle eglSetSwapRectangleANDROID()
+     * We copyback from the front buffer 
+     */
+    if (!dirtyRegion.isEmpty()) {
+        dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
+        if (previousBuffer) {
+            const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
+            if (!copyBack.isEmpty()) {
+                Rect const* list;
+                ssize_t count = copyBack.getRects(&list);
+                // copy from previousBuffer to buffer
+                void* prevBits;
+                if (lock(previousBuffer, 
+                        GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) 
+                {
+                    copyBlt(buffer, bits, previousBuffer, prevBits, list, count);
+                    unlock(previousBuffer);
+                }
+            }
+        }
+        oldDirtyRegion = dirtyRegion;
+    }
 
+    if (previousBuffer) {
+        previousBuffer->common.decRef(&previousBuffer->common); 
+        previousBuffer = 0;
+    }
     
     unlock(buffer);
+    previousBuffer = buffer;
     nativeWindow->queueBuffer(nativeWindow, buffer);
-    buffer->common.decRef(&buffer->common); buffer = 0;
+    buffer = 0;
 
+    // dequeue a new buffer
     nativeWindow->dequeueBuffer(nativeWindow, &buffer);
-    buffer->common.incRef(&buffer->common);
-
+    
     // TODO: lockBuffer should rather be executed when the very first
     // direct rendering occurs.
-    void* vaddr;
     nativeWindow->lockBuffer(nativeWindow, buffer);
-    lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, &bits);
     
+    // reallocate the depth-buffer if needed
     if ((width != buffer->width) || (height != buffer->height)) {
         // TODO: we probably should reset the swap rect here
         // if the window size has changed
@@ -330,11 +496,31 @@
             depth.stride  = buffer->stride;
             depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
             if (depth.data == 0) {
-                setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+                setError(EGL_BAD_ALLOC, EGL_FALSE);
                 return EGL_FALSE;
             }
         }
     }
+    
+    // keep a reference on the buffer
+    buffer->common.incRef(&buffer->common);
+
+    // finally pin the buffer down
+    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
+            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+        LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+                buffer, buffer->width, buffer->height);
+        setError(EGL_BAD_ACCESS, EGL_NO_SURFACE);
+        // FIXME: we should make sure we're not accessing the buffer anymore
+    }
+
+    return EGL_TRUE;
+}
+
+EGLBoolean egl_window_surface_v2_t::setSwapRectangle(
+        EGLint l, EGLint t, EGLint w, EGLint h)
+{
+    dirtyRegion = Rect(l, t, l+w, t+h);
     return EGL_TRUE;
 }
 
@@ -402,12 +588,22 @@
 EGLint egl_window_surface_v2_t::getRefreshRate() const {
     return (60 * EGL_DISPLAY_SCALING); // FIXME
 }
-EGLint egl_window_surface_v2_t::getSwapBehavior() const {
-    //uint32_t flags = nativeWindow->flags;
-    //if (flags & SURFACE_FLAG_PRESERVE_CONTENT)
-    //    return EGL_BUFFER_PRESERVED;
-    // This is now a feature of EGL, currently we don't preserve
-    // the content of the buffers.
+EGLint egl_window_surface_v2_t::getSwapBehavior() const 
+{
+    /*
+     * EGL_BUFFER_PRESERVED means that eglSwapBuffers() completely preserves
+     * the content of the swapped buffer.
+     * 
+     * EGL_BUFFER_DESTROYED means that the content of the buffer is lost.
+     * 
+     * However when ANDROID_swap_retcangle is supported, EGL_BUFFER_DESTROYED
+     * only applies to the area specified by eglSetSwapRectangleANDROID(), that
+     * is, everything outside of this area is preserved.
+     * 
+     * This implementation of EGL assumes the later case.
+     * 
+     */
+
     return EGL_BUFFER_DESTROYED;
 }
 
@@ -581,6 +777,7 @@
         "KHR_image_base "
         // "KHR_image_pixmap "
         "EGL_ANDROID_image_native_buffer "
+        "EGL_ANDROID_swap_rectangle "
         ;
 
 // ----------------------------------------------------------------------------
@@ -1765,3 +1962,23 @@
 
     return EGL_TRUE;
 }
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
+        EGLint left, EGLint top, EGLint width, EGLint height)
+{
+    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+    if (d->dpy != dpy)
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+    // post the surface
+    d->setSwapRectangle(left, top, width, height);
+
+    return EGL_TRUE;
+}
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 74aed20..8c37f2e 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -60,6 +60,7 @@
         "KHR_image_base "
         "KHR_image_pixmap "
         "EGL_ANDROID_image_native_buffer "
+        "EGL_ANDROID_swap_rectangle "
         ;
 
 // ----------------------------------------------------------------------------
@@ -1568,3 +1569,23 @@
 
      return EGL_FALSE;
 }
+
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
+        EGLint left, EGLint top, EGLint width, EGLint height)
+{
+    if (!validate_display_surface(dpy, draw))
+        return EGL_FALSE;    
+    egl_display_t const * const dp = get_display(dpy);
+    egl_surface_t const * const s = get_surface(draw);
+    if (s->cnx->hooks->egl.eglSetSwapRectangleANDROID) {
+        return s->cnx->hooks->egl.eglSetSwapRectangleANDROID(dp->dpys[s->impl],
+                s->surface, left, top, width, height);
+    }
+    return EGL_FALSE;
+}
+
diff --git a/opengl/libs/egl_entries.in b/opengl/libs/egl_entries.in
index 3b4551b..1fe2b57 100644
--- a/opengl/libs/egl_entries.in
+++ b/opengl/libs/egl_entries.in
@@ -50,3 +50,7 @@
 EGL_ENTRY(EGLBoolean,  eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
 EGL_ENTRY(EGLImageKHR, eglCreateImageKHR,   EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint *)
 EGL_ENTRY(EGLBoolean,  eglDestroyImageKHR,  EGLDisplay, EGLImageKHR)
+
+/* ANDROID extensions */
+
+EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)