summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp13
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.h1
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.cpp14
-rw-r--r--opengl/include/EGL/eglext.h12
-rw-r--r--opengl/libagl/egl.cpp269
-rw-r--r--opengl/libs/EGL/egl.cpp21
-rw-r--r--opengl/libs/egl_entries.in4
7 files changed, 298 insertions, 36 deletions
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 @@ void DisplayHardware::init(uint32_t dpy)
* 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 @@ void DisplayHardware::flip(const Region& dirty) const
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 @@ public:
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 @@ void SurfaceFlinger::handleRepaint()
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 @@ void SurfaceFlinger::debugFlashRegions()
{
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 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL
/* 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 @@ struct egl_surface_t
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::getRefreshRate() const {
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 @@ struct egl_window_surface_v2_t : public egl_surface_t
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 @@ egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
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 @@ egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
}
}
+ // 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 @@ status_t egl_window_surface_v2_t::unlock(android_native_buffer_t* buf)
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 @@ EGLBoolean egl_window_surface_v2_t::swapBuffers()
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::getVerticalResolution() const {
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 @@ static char const * const gExtensionsString =
"KHR_image_base "
// "KHR_image_pixmap "
"EGL_ANDROID_image_native_buffer "
+ "EGL_ANDROID_swap_rectangle "
;
// ----------------------------------------------------------------------------
@@ -1765,3 +1962,23 @@ EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
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 @@ static char const * const gExtensionString =
"KHR_image_base "
"KHR_image_pixmap "
"EGL_ANDROID_image_native_buffer "
+ "EGL_ANDROID_swap_rectangle "
;
// ----------------------------------------------------------------------------
@@ -1568,3 +1569,23 @@ EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
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, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint
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)