merge in KQS81M
diff --git a/data/etc/android.hardware.consumerir.xml b/data/etc/android.hardware.consumerir.xml
new file mode 100644
index 0000000..adba2f9
--- /dev/null
+++ b/data/etc/android.hardware.consumerir.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!-- This is the standard feature indicating that the device can communicate
+ using Near-Field Communications (NFC). -->
+<permissions>
+ <feature name="android.hardware.consumerir" />
+</permissions>
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 75ee30c..a5fdfb9 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -273,7 +273,7 @@
private:
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
- const sp<GraphicBuffer>& graphicBuffer);
+ const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
@@ -386,6 +386,10 @@
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
+ // mCropRect is the crop rectangle passed to EGL when mEglImage was
+ // created.
+ Rect mCropRect;
+
// mFence is the EGL sync object that must signal before the buffer
// associated with this buffer slot may be dequeued. It is initialized
// to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h
index ac67f94..de3aeb1 100644
--- a/include/media/hardware/HardwareAPI.h
+++ b/include/media/hardware/HardwareAPI.h
@@ -89,6 +89,31 @@
buffer_handle_t pHandle;
};
+// A pointer to this struct is passed to OMX_SetParameter() when the extension
+// index "OMX.google.android.index.prepareForAdaptivePlayback" is given.
+//
+// This method is used to signal a video decoder, that the user has requested
+// seamless resolution change support (if bEnable is set to OMX_TRUE).
+// nMaxFrameWidth and nMaxFrameHeight are the dimensions of the largest
+// anticipated frames in the video. If bEnable is OMX_FALSE, no resolution
+// change is expected, and the nMaxFrameWidth/Height fields are unused.
+//
+// If the decoder supports dynamic output buffers, it may ignore this
+// request. Otherwise, it shall request resources in such a way so that it
+// avoids full port-reconfiguration (due to output port-definition change)
+// during resolution changes.
+//
+// DO NOT USE THIS STRUCTURE AS IT WILL BE REMOVED. INSTEAD, IMPLEMENT
+// METADATA SUPPORT FOR VIDEO DECODERS.
+struct PrepareForAdaptivePlaybackParams {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_BOOL bEnable;
+ OMX_U32 nMaxFrameWidth;
+ OMX_U32 nMaxFrameHeight;
+};
+
// A pointer to this struct is passed to OMX_SetParameter when the extension
// index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is
// given. This call will only be performed if a prior call was made with the
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
index e21e6a8..2f4c3c4 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -32,6 +32,8 @@
virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
const String16& packageName) = 0;
+ virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
+ const String16& packageName, int uid) = 0;
virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags) = 0;
};
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 87d66e2..c165a68 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -668,11 +668,15 @@
mConnectedApi = api;
output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size());
- // set-up a death notification so that we can disconnect automatically
- // when/if the remote producer dies.
- // This will fail with INVALID_OPERATION if the "token" is local to our process.
- if (token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)) == NO_ERROR) {
- mConnectedProducerToken = token;
+ // set-up a death notification so that we can disconnect
+ // automatically when/if the remote producer dies.
+ if (token != NULL && token->remoteBinder() != NULL) {
+ status_t err = token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
+ if (err == NO_ERROR) {
+ mConnectedProducerToken = token;
+ } else {
+ ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err);
+ }
}
}
break;
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index cf3f12a..7ee3081 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -41,6 +41,9 @@
#include <utils/String8.h>
#include <utils/Trace.h>
+EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+#define CROP_EXT_STR "EGL_ANDROID_image_crop"
+
namespace android {
// Macros for including the GLConsumer name in log messages
@@ -89,6 +92,30 @@
Mutex GLConsumer::sStaticInitLock;
sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer;
+static bool hasEglAndroidImageCropImpl() {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
+ size_t cropExtLen = strlen(CROP_EXT_STR);
+ size_t extsLen = strlen(exts);
+ bool equal = !strcmp(CROP_EXT_STR, exts);
+ bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1);
+ bool atEnd = (cropExtLen+1) < extsLen &&
+ !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1));
+ bool inMiddle = strstr(exts, " " CROP_EXT_STR " ");
+ return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglAndroidImageCrop() {
+ // Only compute whether the extension is present once the first time this
+ // function is called.
+ static bool hasIt = hasEglAndroidImageCropImpl();
+ return hasIt;
+}
+
+static bool isEglImageCroppable(const Rect& crop) {
+ return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0);
+}
+
GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp),
@@ -279,19 +306,30 @@
}
int slot = item->mBuf;
- if (item->mGraphicBuffer != NULL) {
- // This buffer has not been acquired before, so we must assume
- // that any EGLImage in mEglSlots is stale.
- if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
- if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
- ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
- slot);
- // keep going
- }
- mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
+ bool destroyEglImage = false;
+
+ if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
+ if (item->mGraphicBuffer != NULL) {
+ // This buffer has not been acquired before, so we must assume
+ // that any EGLImage in mEglSlots is stale.
+ destroyEglImage = true;
+ } else if (mEglSlots[slot].mCropRect != item->mCrop) {
+ // We've already seen this buffer before, but it now has a
+ // different crop rect, so we'll need to recreate the EGLImage if
+ // we're using the EGL_ANDROID_image_crop extension.
+ destroyEglImage = hasEglAndroidImageCrop();
}
}
+ if (destroyEglImage) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
+ ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
+ slot);
+ // keep going
+ }
+ mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
+ }
+
return NO_ERROR;
}
@@ -334,13 +372,15 @@
// EGLImage when detaching from a context but the buffer has not been
// re-allocated.
if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
- EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
+ EGLImageKHR image = createImage(mEglDisplay,
+ mSlots[buf].mGraphicBuffer, item.mCrop);
if (image == EGL_NO_IMAGE_KHR) {
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
mEglDisplay, buf);
return UNKNOWN_ERROR;
}
mEglSlots[buf].mEglImage = image;
+ mEglSlots[buf].mCropRect = item.mCrop;
}
// Do whatever sync ops we need to do before releasing the old slot.
@@ -581,7 +621,8 @@
mCurrentTexture, mCurrentTextureBuf.get());
// Create a temporary EGLImageKHR.
- EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
+ Rect crop;
+ EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop);
if (image == EGL_NO_IMAGE_KHR) {
return UNKNOWN_ERROR;
}
@@ -753,60 +794,66 @@
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
}
- Rect cropRect = mCurrentCrop;
- float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
- float bufferWidth = buf->getWidth();
- float bufferHeight = buf->getHeight();
- if (!cropRect.isEmpty()) {
- float shrinkAmount = 0.0f;
- if (mFilteringEnabled) {
- // In order to prevent bilinear sampling beyond the edge of the
- // crop rectangle we may need to shrink it by 2 texels in each
- // dimension. Normally this would just need to take 1/2 a texel
- // off each end, but because the chroma channels of YUV420 images
- // are subsampled we may need to shrink the crop region by a whole
- // texel on each side.
- switch (buf->getPixelFormat()) {
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_RGBX_8888:
- case PIXEL_FORMAT_RGB_888:
- case PIXEL_FORMAT_RGB_565:
- case PIXEL_FORMAT_BGRA_8888:
- // We know there's no subsampling of any channels, so we
- // only need to shrink by a half a pixel.
- shrinkAmount = 0.5;
- break;
+ float mtxBeforeFlipV[16];
+ if (!isEglImageCroppable(mCurrentCrop)) {
+ Rect cropRect = mCurrentCrop;
+ float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+ float bufferWidth = buf->getWidth();
+ float bufferHeight = buf->getHeight();
+ if (!cropRect.isEmpty()) {
+ float shrinkAmount = 0.0f;
+ if (mFilteringEnabled) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
- default:
- // If we don't recognize the format, we must assume the
- // worst case (that we care about), which is YUV420.
- shrinkAmount = 1.0;
- break;
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
+ }
+ }
+
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
+ bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
+ bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
+ bufferHeight;
}
}
+ float crop[16] = {
+ sx, 0, 0, 0,
+ 0, sy, 0, 0,
+ 0, 0, 1, 0,
+ tx, ty, 0, 1,
+ };
- // Only shrink the dimensions that are not the size of the buffer.
- if (cropRect.width() < bufferWidth) {
- tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
- sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
- bufferWidth;
- }
- if (cropRect.height() < bufferHeight) {
- ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
- bufferHeight;
- sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
- bufferHeight;
+ mtxMul(mtxBeforeFlipV, crop, xform);
+ } else {
+ for (int i = 0; i < 16; i++) {
+ mtxBeforeFlipV[i] = xform[i];
}
}
- float crop[16] = {
- sx, 0, 0, 0,
- 0, sy, 0, 0,
- 0, 0, 1, 0,
- tx, ty, 0, 1,
- };
-
- float mtxBeforeFlipV[16];
- mtxMul(mtxBeforeFlipV, crop, xform);
// SurfaceFlinger expects the top of its window textures to be at a Y
// coordinate of 0, so GLConsumer must behave the same way. We don't
@@ -828,12 +875,26 @@
}
EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
- const sp<GraphicBuffer>& graphicBuffer) {
+ const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = {
- EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_IMAGE_CROP_LEFT_ANDROID, crop.left,
+ EGL_IMAGE_CROP_TOP_ANDROID, crop.top,
+ EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right,
+ EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom,
EGL_NONE,
};
+ if (!crop.isValid()) {
+ // No crop rect to set, so terminate the attrib array before the crop.
+ attrs[2] = EGL_NONE;
+ } else if (!isEglImageCroppable(crop)) {
+ // The crop rect is not at the origin, so we can't set the crop on the
+ // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
+ // extension. In the future we can add a layered extension that
+ // removes this restriction if there is hardware that can support it.
+ attrs[2] = EGL_NONE;
+ }
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
if (image == EGL_NO_IMAGE_KHR) {
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 6c505ed..3b2984a 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -494,6 +494,14 @@
#define EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147
#endif
+#ifndef EGL_ANDROID_image_crop
+#define EGL_ANDROID_image_crop 1
+#define EGL_IMAGE_CROP_LEFT_ANDROID 0x3148
+#define EGL_IMAGE_CROP_TOP_ANDROID 0x3149
+#define EGL_IMAGE_CROP_RIGHT_ANDROID 0x314A
+#define EGL_IMAGE_CROP_BOTTOM_ANDROID 0x314B
+#endif
+
#ifndef EGL_ANDROID_blob_cache
#define EGL_ANDROID_blob_cache 1
typedef khronos_ssize_t EGLsizeiANDROID;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 04d5f45..0cc5265 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -99,6 +99,7 @@
"EGL_NV_system_time "
"EGL_ANDROID_image_native_buffer " // mandatory
"EGL_KHR_wait_sync " // strongly recommended
+ "EGL_ANDROID_recordable " // mandatory
;
// extensions not exposed to applications but used by the ANDROID system
@@ -106,8 +107,7 @@
// "EGL_IMG_hibernate_process " // optional
// "EGL_ANDROID_native_fence_sync " // strongly recommended
// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
-// "EGL_ANDROID_recordable " // mandatory
-
+// "EGL_ANDROID_image_crop " // optional
/*
* EGL Extensions entry-points exposed to 3rd party applications
diff --git a/opengl/specs/README b/opengl/specs/README
index eb86869..f4de1b3 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -14,4 +14,8 @@
0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync)
0x3147 EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target)
-0x3148 - 0x314F (unused)
+0x3148 EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop)
+0x3149 EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop)
+0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
+0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
+0x314C - 0x314F (unused)
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
index 3f5b81e..9f60e75 100644
--- a/services/powermanager/IPowerManager.cpp
+++ b/services/powermanager/IPowerManager.cpp
@@ -30,7 +30,8 @@
// must be kept in sync with IPowerManager.aidl
enum {
ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION,
- RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 1,
+ ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1,
+ RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2,
};
class BpPowerManager : public BpInterface<IPowerManager>
@@ -55,6 +56,20 @@
return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply);
}
+ virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
+ const String16& packageName, int uid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
+
+ data.writeStrongBinder(lock);
+ data.writeInt32(flags);
+ data.writeString16(tag);
+ data.writeString16(packageName);
+ data.writeInt32(uid); // uid to blame for the work
+ return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply);
+ }
+
virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags)
{
Parcel data, reply;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 7cc4ce1..bd89bd5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES:= \
Client.cpp \
DisplayDevice.cpp \
+ DispSync.cpp \
EventThread.cpp \
FrameTracker.cpp \
Layer.cpp \
@@ -53,6 +54,23 @@
LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS)
endif
+ifeq ($(TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK),true)
+ LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK
+endif
+
+# See build/target/board/generic/BoardConfig.mk for a description of this setting.
+ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
+ LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
+else
+ LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=0
+endif
+
+ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),)
+ LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=$(PRESENT_TIME_OFFSET_FROM_VSYNC_NS)
+else
+ LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=0
+endif
+
LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_SHARED_LIBRARIES := \
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
new file mode 100644
index 0000000..7e67138
--- /dev/null
+++ b/services/surfaceflinger/DispSync.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <math.h>
+
+#include <cutils/log.h>
+
+#include <ui/Fence.h>
+
+#include <utils/String8.h>
+#include <utils/Thread.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include "DispSync.h"
+#include "EventLog/EventLog.h"
+
+namespace android {
+
+// Setting this to true enables verbose tracing that can be used to debug
+// vsync event model or phase issues.
+static const bool traceDetailedInfo = false;
+
+// This is the threshold used to determine when hardware vsync events are
+// needed to re-synchronize the software vsync model with the hardware. The
+// error metric used is the mean of the squared difference between each
+// present time and the nearest software-predicted vsync.
+static const nsecs_t errorThreshold = 160000000000;
+
+// This works around the lack of support for the sync framework on some
+// devices.
+#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK
+static const bool runningWithoutSyncFramework = true;
+#else
+static const bool runningWithoutSyncFramework = false;
+#endif
+
+// This is the offset from the present fence timestamps to the corresponding
+// vsync event.
+static const int64_t presentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
+
+class DispSyncThread: public Thread {
+public:
+
+ DispSyncThread():
+ mStop(false),
+ mPeriod(0),
+ mPhase(0),
+ mWakeupLatency(0) {
+ }
+
+ virtual ~DispSyncThread() {}
+
+ void updateModel(nsecs_t period, nsecs_t phase) {
+ Mutex::Autolock lock(mMutex);
+ mPeriod = period;
+ mPhase = phase;
+ mCond.signal();
+ }
+
+ void stop() {
+ Mutex::Autolock lock(mMutex);
+ mStop = true;
+ mCond.signal();
+ }
+
+ virtual bool threadLoop() {
+ status_t err;
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t nextEventTime = 0;
+
+ while (true) {
+ Vector<CallbackInvocation> callbackInvocations;
+
+ nsecs_t targetTime = 0;
+
+ { // Scope for lock
+ Mutex::Autolock lock(mMutex);
+
+ if (mStop) {
+ return false;
+ }
+
+ if (mPeriod == 0) {
+ err = mCond.wait(mMutex);
+ if (err != NO_ERROR) {
+ ALOGE("error waiting for new events: %s (%d)",
+ strerror(-err), err);
+ return false;
+ }
+ continue;
+ }
+
+ nextEventTime = computeNextEventTimeLocked(now);
+ targetTime = nextEventTime - mWakeupLatency;
+
+ bool isWakeup = false;
+
+ if (now < targetTime) {
+ err = mCond.waitRelative(mMutex, targetTime - now);
+
+ if (err == TIMED_OUT) {
+ isWakeup = true;
+ } else if (err != NO_ERROR) {
+ ALOGE("error waiting for next event: %s (%d)",
+ strerror(-err), err);
+ return false;
+ }
+ }
+
+ now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ if (isWakeup) {
+ mWakeupLatency = ((mWakeupLatency * 63) +
+ (now - targetTime)) / 64;
+ if (mWakeupLatency > 500000) {
+ // Don't correct by more than 500 us
+ mWakeupLatency = 500000;
+ }
+ if (traceDetailedInfo) {
+ ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
+ ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
+ }
+ }
+
+ callbackInvocations = gatherCallbackInvocationsLocked(now);
+ }
+
+ if (callbackInvocations.size() > 0) {
+ fireCallbackInvocations(callbackInvocations);
+ }
+ }
+
+ return false;
+ }
+
+ status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ if (mEventListeners[i].mCallback == callback) {
+ return BAD_VALUE;
+ }
+ }
+
+ EventListener listener;
+ listener.mPhase = phase;
+ listener.mCallback = callback;
+ listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mEventListeners.push(listener);
+
+ mCond.signal();
+
+ return NO_ERROR;
+ }
+
+ status_t removeEventListener(const sp<DispSync::Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ if (mEventListeners[i].mCallback == callback) {
+ mEventListeners.removeAt(i);
+ mCond.signal();
+ return NO_ERROR;
+ }
+ }
+
+ return BAD_VALUE;
+ }
+
+ // This method is only here to handle the runningWithoutSyncFramework
+ // case.
+ bool hasAnyEventListeners() {
+ Mutex::Autolock lock(mMutex);
+ return !mEventListeners.empty();
+ }
+
+private:
+
+ struct EventListener {
+ nsecs_t mPhase;
+ nsecs_t mLastEventTime;
+ sp<DispSync::Callback> mCallback;
+ };
+
+ struct CallbackInvocation {
+ sp<DispSync::Callback> mCallback;
+ nsecs_t mEventTime;
+ };
+
+ nsecs_t computeNextEventTimeLocked(nsecs_t now) {
+ nsecs_t nextEventTime = INT64_MAX;
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
+ now);
+
+ if (t < nextEventTime) {
+ nextEventTime = t;
+ }
+ }
+
+ return nextEventTime;
+ }
+
+ Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+ Vector<CallbackInvocation> callbackInvocations;
+ nsecs_t ref = now - mPeriod;
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
+ ref);
+
+ if (t - mWakeupLatency < now) {
+ CallbackInvocation ci;
+ ci.mCallback = mEventListeners[i].mCallback;
+ ci.mEventTime = t;
+ callbackInvocations.push(ci);
+ mEventListeners.editItemAt(i).mLastEventTime = t;
+ }
+ }
+
+ return callbackInvocations;
+ }
+
+ nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
+ nsecs_t ref) {
+
+ nsecs_t lastEventTime = listener.mLastEventTime;
+ if (ref < lastEventTime) {
+ ref = lastEventTime;
+ }
+
+ nsecs_t phase = mPhase + listener.mPhase;
+ nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase;
+
+ if (t - listener.mLastEventTime < mPeriod / 2) {
+ t += mPeriod;
+ }
+
+ return t;
+ }
+
+ void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
+ for (size_t i = 0; i < callbacks.size(); i++) {
+ callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
+ }
+ }
+
+ bool mStop;
+
+ nsecs_t mPeriod;
+ nsecs_t mPhase;
+ nsecs_t mWakeupLatency;
+
+ Vector<EventListener> mEventListeners;
+
+ Mutex mMutex;
+ Condition mCond;
+};
+
+class ZeroPhaseTracer : public DispSync::Callback {
+public:
+ ZeroPhaseTracer() : mParity(false) {}
+
+ virtual void onDispSyncEvent(nsecs_t when) {
+ mParity = !mParity;
+ ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
+ }
+
+private:
+ bool mParity;
+};
+
+DispSync::DispSync() {
+ mThread = new DispSyncThread();
+ mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+
+ reset();
+ beginResync();
+
+ if (traceDetailedInfo) {
+ // If runningWithoutSyncFramework is true then the ZeroPhaseTracer
+ // would prevent HW vsync event from ever being turned off.
+ // Furthermore the zero-phase tracing is not needed because any time
+ // there is an event registered we will turn on the HW vsync events.
+ if (!runningWithoutSyncFramework) {
+ addEventListener(0, new ZeroPhaseTracer());
+ }
+ }
+}
+
+DispSync::~DispSync() {}
+
+void DispSync::reset() {
+ Mutex::Autolock lock(mMutex);
+
+ mNumResyncSamples = 0;
+ mFirstResyncSample = 0;
+ mNumResyncSamplesSincePresent = 0;
+ resetErrorLocked();
+}
+
+bool DispSync::addPresentFence(const sp<Fence>& fence) {
+ Mutex::Autolock lock(mMutex);
+
+ mPresentFences[mPresentSampleOffset] = fence;
+ mPresentTimes[mPresentSampleOffset] = 0;
+ mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
+ mNumResyncSamplesSincePresent = 0;
+
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ const sp<Fence>& f(mPresentFences[i]);
+ if (f != NULL) {
+ nsecs_t t = f->getSignalTime();
+ if (t < INT64_MAX) {
+ mPresentFences[i].clear();
+ mPresentTimes[i] = t + presentTimeOffset;
+ }
+ }
+ }
+
+ updateErrorLocked();
+
+ return mPeriod == 0 || mError > errorThreshold;
+}
+
+void DispSync::beginResync() {
+ Mutex::Autolock lock(mMutex);
+
+ mNumResyncSamples = 0;
+}
+
+bool DispSync::addResyncSample(nsecs_t timestamp) {
+ Mutex::Autolock lock(mMutex);
+
+ size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
+ mResyncSamples[idx] = timestamp;
+
+ if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
+ mNumResyncSamples++;
+ } else {
+ mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
+ }
+
+ updateModelLocked();
+
+ if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
+ resetErrorLocked();
+ }
+
+ if (runningWithoutSyncFramework) {
+ // If we don't have the sync framework we will never have
+ // addPresentFence called. This means we have no way to know whether
+ // or not we're synchronized with the HW vsyncs, so we just request
+ // that the HW vsync events be turned on whenever we need to generate
+ // SW vsync events.
+ return mThread->hasAnyEventListeners();
+ }
+
+ return mPeriod == 0 || mError > errorThreshold;
+}
+
+void DispSync::endResync() {
+}
+
+status_t DispSync::addEventListener(nsecs_t phase,
+ const sp<Callback>& callback) {
+
+ Mutex::Autolock lock(mMutex);
+ return mThread->addEventListener(phase, callback);
+}
+
+status_t DispSync::removeEventListener(const sp<Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+ return mThread->removeEventListener(callback);
+}
+
+void DispSync::setPeriod(nsecs_t period) {
+ Mutex::Autolock lock(mMutex);
+ mPeriod = period;
+ mPhase = 0;
+}
+
+void DispSync::updateModelLocked() {
+ if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
+ nsecs_t durationSum = 0;
+ for (size_t i = 1; i < mNumResyncSamples; i++) {
+ size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+ size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
+ durationSum += mResyncSamples[idx] - mResyncSamples[prev];
+ }
+
+ mPeriod = durationSum / (mNumResyncSamples - 1);
+
+ double sampleAvgX = 0;
+ double sampleAvgY = 0;
+ double scale = 2.0 * M_PI / double(mPeriod);
+ for (size_t i = 0; i < mNumResyncSamples; i++) {
+ size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+ nsecs_t sample = mResyncSamples[idx];
+ double samplePhase = double(sample % mPeriod) * scale;
+ sampleAvgX += cos(samplePhase);
+ sampleAvgY += sin(samplePhase);
+ }
+
+ sampleAvgX /= double(mNumResyncSamples);
+ sampleAvgY /= double(mNumResyncSamples);
+
+ mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
+
+ if (mPhase < 0) {
+ mPhase += mPeriod;
+ }
+
+ if (traceDetailedInfo) {
+ ATRACE_INT64("DispSync:Period", mPeriod);
+ ATRACE_INT64("DispSync:Phase", mPhase);
+ }
+
+ mThread->updateModel(mPeriod, mPhase);
+ }
+}
+
+void DispSync::updateErrorLocked() {
+ if (mPeriod == 0) {
+ return;
+ }
+
+ int numErrSamples = 0;
+ nsecs_t sqErrSum = 0;
+
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ nsecs_t sample = mPresentTimes[i];
+ if (sample > mPhase) {
+ nsecs_t sampleErr = (sample - mPhase) % mPeriod;
+ if (sampleErr > mPeriod / 2) {
+ sampleErr -= mPeriod;
+ }
+ sqErrSum += sampleErr * sampleErr;
+ numErrSamples++;
+ }
+ }
+
+ if (numErrSamples > 0) {
+ mError = sqErrSum / numErrSamples;
+ } else {
+ mError = 0;
+ }
+
+ if (traceDetailedInfo) {
+ ATRACE_INT64("DispSync:Error", mError);
+ }
+}
+
+void DispSync::resetErrorLocked() {
+ mPresentSampleOffset = 0;
+ mError = 0;
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ mPresentFences[i].clear();
+ mPresentTimes[i] = 0;
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
new file mode 100644
index 0000000..c4280aa
--- /dev/null
+++ b/services/surfaceflinger/DispSync.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 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_DISPSYNC_H
+#define ANDROID_DISPSYNC_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class String8;
+class Fence;
+class DispSyncThread;
+
+// DispSync maintains a model of the periodic hardware-based vsync events of a
+// display and uses that model to execute period callbacks at specific phase
+// offsets from the hardware vsync events. The model is constructed by
+// feeding consecutive hardware event timestamps to the DispSync object via
+// the addResyncSample method.
+//
+// The model is validated using timestamps from Fence objects that are passed
+// to the DispSync object via the addPresentFence method. These fence
+// timestamps should correspond to a hardware vsync event, but they need not
+// be consecutive hardware vsync times. If this method determines that the
+// current model accurately represents the hardware event times it will return
+// false to indicate that a resynchronization (via addResyncSample) is not
+// needed.
+class DispSync {
+
+public:
+
+ class Callback: public virtual RefBase {
+ public:
+ virtual ~Callback() {};
+ virtual void onDispSyncEvent(nsecs_t when) = 0;
+ };
+
+ DispSync();
+ ~DispSync();
+
+ void reset();
+
+ // addPresentFence adds a fence for use in validating the current vsync
+ // event model. The fence need not be signaled at the time
+ // addPresentFence is called. When the fence does signal, its timestamp
+ // should correspond to a hardware vsync event. Unlike the
+ // addResyncSample method, the timestamps of consecutive fences need not
+ // correspond to consecutive hardware vsync events.
+ //
+ // This method should be called with the retire fence from each HWComposer
+ // set call that affects the display.
+ bool addPresentFence(const sp<Fence>& fence);
+
+ // The beginResync, addResyncSample, and endResync methods are used to re-
+ // synchronize the DispSync's model to the hardware vsync events. The re-
+ // synchronization process involves first calling beginResync, then
+ // calling addResyncSample with a sequence of consecutive hardware vsync
+ // event timestamps, and finally calling endResync when addResyncSample
+ // indicates that no more samples are needed by returning false.
+ //
+ // This resynchronization process should be performed whenever the display
+ // is turned on (i.e. once immediately after it's turned on) and whenever
+ // addPresentFence returns true indicating that the model has drifted away
+ // from the hardware vsync events.
+ void beginResync();
+ bool addResyncSample(nsecs_t timestamp);
+ void endResync();
+
+ // The setPreiod method sets the vsync event model's period to a specific
+ // value. This should be used to prime the model when a display is first
+ // turned on. It should NOT be used after that.
+ void setPeriod(nsecs_t period);
+
+ // addEventListener registers a callback to be called repeatedly at the
+ // given phase offset from the hardware vsync events. The callback is
+ // called from a separate thread and it should return reasonably quickly
+ // (i.e. within a few hundred microseconds).
+ status_t addEventListener(nsecs_t phase, const sp<Callback>& callback);
+
+ // removeEventListener removes an already-registered event callback. Once
+ // this method returns that callback will no longer be called by the
+ // DispSync object.
+ status_t removeEventListener(const sp<Callback>& callback);
+
+private:
+
+ void updateModelLocked();
+ void updateErrorLocked();
+ void resetErrorLocked();
+
+ enum { MAX_RESYNC_SAMPLES = 32 };
+ enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 };
+ enum { NUM_PRESENT_SAMPLES = 8 };
+ enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 12 };
+
+ // mPeriod is the computed period of the modeled vsync events in
+ // nanoseconds.
+ nsecs_t mPeriod;
+
+ // mPhase is the phase offset of the modeled vsync events. It is the
+ // number of nanoseconds from time 0 to the first vsync event.
+ nsecs_t mPhase;
+
+ // mError is the computed model error. It is based on the difference
+ // between the estimated vsync event times and those observed in the
+ // mPresentTimes array.
+ nsecs_t mError;
+
+ // These member variables are the state used during the resynchronization
+ // process to store information about the hardware vsync event times used
+ // to compute the model.
+ nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+ size_t mFirstResyncSample;
+ size_t mNumResyncSamples;
+ int mNumResyncSamplesSincePresent;
+
+ // These member variables store information about the present fences used
+ // to validate the currently computed model.
+ sp<Fence> mPresentFences[NUM_PRESENT_SAMPLES];
+ nsecs_t mPresentTimes[NUM_PRESENT_SAMPLES];
+ size_t mPresentSampleOffset;
+
+ // mThread is the thread from which all the callbacks are called.
+ sp<DispSyncThread> mThread;
+
+ // mMutex is used to protect access to all member variables.
+ mutable Mutex mMutex;
+};
+
+}
+
+#endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 32c55fd..179d956 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -285,7 +285,7 @@
void HWComposer::vsync(int disp, int64_t timestamp) {
if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
char tag[16];
- snprintf(tag, sizeof(tag), "VSYNC_%1u", disp);
+ snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
mEventHandler.onVSyncReceived(disp, timestamp);
@@ -524,7 +524,14 @@
disp.framebufferTarget->handle = disp.fbTargetHandle;
disp.framebufferTarget->transform = 0;
disp.framebufferTarget->blending = HWC_BLENDING_PREMULT;
- disp.framebufferTarget->sourceCrop = r;
+ if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
+ disp.framebufferTarget->sourceCropf.left = 0;
+ disp.framebufferTarget->sourceCropf.top = 0;
+ disp.framebufferTarget->sourceCropf.right = disp.width;
+ disp.framebufferTarget->sourceCropf.bottom = disp.height;
+ } else {
+ disp.framebufferTarget->sourceCrop = r;
+ }
disp.framebufferTarget->displayFrame = r;
disp.framebufferTarget->visibleRegionScreen.numRects = 1;
disp.framebufferTarget->visibleRegionScreen.rects =
@@ -604,6 +611,10 @@
// here we're just making sure that "skip" layers are set
// to HWC_FRAMEBUFFER and we're also counting how many layers
// we have of each type.
+ //
+ // If there are no window layers, we treat the display has having FB
+ // composition, because SurfaceFlinger will use GLES to draw the
+ // wormhole region.
for (size_t i=0 ; i<mNumDisplays ; i++) {
DisplayData& disp(mDisplayData[i]);
disp.hasFbComp = false;
@@ -625,6 +636,11 @@
disp.hasOvComp = true;
}
}
+ if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {
+ disp.hasFbComp = true;
+ }
+ } else {
+ disp.hasFbComp = true;
}
}
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index c06043d..c5a14b0 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -147,6 +147,10 @@
mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
+ // At this point we know the output buffer acquire fence,
+ // so update HWC state with it.
+ mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
+
return mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
}
@@ -258,6 +262,17 @@
Source source = fbSourceForCompositionType(mCompositionType);
if (source == SOURCE_SINK) {
+
+ if (mOutputProducerSlot < 0) {
+ // Last chance bailout if something bad happened earlier. For example,
+ // in a GLES configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // will soldier on. So we end up here without a buffer. There should
+ // be lots of scary messages in the log just before this.
+ VDS_LOGE("dequeueBuffer: no buffer, bailing out");
+ return NO_MEMORY;
+ }
+
// We already dequeued the output buffer. If the GLES driver wants
// something incompatible, we have to cancel and get a new one. This
// will mean that HWC will see a different output buffer between
@@ -404,7 +419,11 @@
return result;
mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
- result = mHwc.setOutputBuffer(mDisplayId, mOutputFence,
+ // On GLES-only frames, we don't have the right output buffer acquire fence
+ // until after GLES calls queueBuffer(). So here we just set the buffer
+ // (for use in HWC prepare) but not the fence; we'll call this again with
+ // the proper fence once we have it.
+ result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
mProducerBuffers[mOutputProducerSlot]);
return result;
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index a61ad72..3528b62 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -36,9 +36,10 @@
namespace android {
// ---------------------------------------------------------------------------
-EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
- : mFlinger(flinger),
+EventThread::EventThread(const sp<VSyncSource>& src)
+ : mVSyncSource(src),
mUseSoftwareVSync(false),
+ mVsyncEnabled(false),
mDebugVsyncEnabled(false) {
for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
@@ -110,19 +111,13 @@
}
}
-
-void EventThread::onVSyncReceived(int type, nsecs_t timestamp) {
- ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
- "received vsync event for an invalid display (id=%d)", type);
-
+void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
- if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- mVSyncEvent[type].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[type].header.id = type;
- mVSyncEvent[type].header.timestamp = timestamp;
- mVSyncEvent[type].vsync.count++;
- mCondition.broadcast();
- }
+ mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ mVSyncEvent[0].header.id = 0;
+ mVSyncEvent[0].header.timestamp = timestamp;
+ mVSyncEvent[0].vsync.count++;
+ mCondition.broadcast();
}
void EventThread::onHotplugReceived(int type, bool connected) {
@@ -308,18 +303,23 @@
void EventThread::enableVSyncLocked() {
if (!mUseSoftwareVSync) {
// never enable h/w VSYNC when screen is off
- mFlinger->eventControl(DisplayDevice::DISPLAY_PRIMARY,
- SurfaceFlinger::EVENT_VSYNC, true);
- mPowerHAL.vsyncHint(true);
+ if (!mVsyncEnabled) {
+ mVsyncEnabled = true;
+ mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
+ mVSyncSource->setVSyncEnabled(true);
+ mPowerHAL.vsyncHint(true);
+ }
}
mDebugVsyncEnabled = true;
}
void EventThread::disableVSyncLocked() {
- mFlinger->eventControl(DisplayDevice::DISPLAY_PRIMARY,
- SurfaceFlinger::EVENT_VSYNC, false);
- mPowerHAL.vsyncHint(false);
- mDebugVsyncEnabled = false;
+ if (mVsyncEnabled) {
+ mVsyncEnabled = false;
+ mVSyncSource->setVSyncEnabled(false);
+ mPowerHAL.vsyncHint(false);
+ mDebugVsyncEnabled = false;
+ }
}
void EventThread::dump(String8& result) const {
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 5e88693..f6ab4a7 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -39,7 +39,21 @@
// ---------------------------------------------------------------------------
-class EventThread : public Thread {
+
+class VSyncSource : public virtual RefBase {
+public:
+ class Callback: public virtual RefBase {
+ public:
+ virtual ~Callback() {}
+ virtual void onVSyncEvent(nsecs_t when) = 0;
+ };
+
+ virtual ~VSyncSource() {}
+ virtual void setVSyncEnabled(bool enable) = 0;
+ virtual void setCallback(const sp<Callback>& callback) = 0;
+};
+
+class EventThread : public Thread, private VSyncSource::Callback {
class Connection : public BnDisplayEventConnection {
public:
Connection(const sp<EventThread>& eventThread);
@@ -62,7 +76,7 @@
public:
- EventThread(const sp<SurfaceFlinger>& flinger);
+ EventThread(const sp<VSyncSource>& src);
sp<Connection> createEventConnection() const;
status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -76,8 +90,7 @@
// called after the screen is turned on from main thread
void onScreenAcquired();
- // called when receiving a vsync event
- void onVSyncReceived(int type, nsecs_t timestamp);
+ // called when receiving a hotplug event
void onHotplugReceived(int type, bool connected);
Vector< sp<EventThread::Connection> > waitForEvent(
@@ -89,12 +102,14 @@
virtual bool threadLoop();
virtual void onFirstRef();
+ virtual void onVSyncEvent(nsecs_t timestamp);
+
void removeDisplayEventConnection(const wp<Connection>& connection);
void enableVSyncLocked();
void disableVSyncLocked();
// constants
- sp<SurfaceFlinger> mFlinger;
+ sp<VSyncSource> mVSyncSource;
PowerHAL mPowerHAL;
mutable Mutex mLock;
@@ -105,6 +120,7 @@
Vector< DisplayEventReceiver::Event > mPendingEvents;
DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
bool mUseSoftwareVSync;
+ bool mVsyncEnabled;
// for debugging
bool mDebugVsyncEnabled;
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 46f628d..ba82cad 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -30,9 +30,25 @@
// ---------------------------------------------------------------------------
RenderEngine* RenderEngine::create(EGLDisplay display, EGLConfig config) {
+ EGLint renderableType = 0;
+ EGLint contextClientVersion = 0;
+
+ // query the renderable type, setting the EGL_CONTEXT_CLIENT_VERSION accordingly
+ if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+
+ if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
// Also create our EGLContext
EGLint contextAttributes[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2, // MUST be first
+ EGL_CONTEXT_CLIENT_VERSION, contextClientVersion, // MUST be first
#ifdef EGL_IMG_context_priority
#ifdef HAS_CONTEXT_PRIORITY
#warning "using EGL_IMG_context_priority"
@@ -41,13 +57,7 @@
#endif
EGL_NONE, EGL_NONE
};
-
EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes);
- if (ctxt == EGL_NO_CONTEXT) {
- // maybe ES 2.x is not supported
- ALOGW("can't create an ES 2.x context, trying 1.x");
- ctxt = eglCreateContext(display, config, NULL, contextAttributes + 2);
- }
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -191,6 +201,10 @@
glDeleteTextures(count, names);
}
+void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
+ glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+}
+
void RenderEngine::dump(String8& result) {
const GLExtensions& extensions(GLExtensions::getInstance());
result.appendFormat("GLES: %s, %s, %s\n",
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 5f331d4..3c7f9ab 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -70,6 +70,7 @@
void disableScissor();
void genTextures(size_t count, uint32_t* names);
void deleteTextures(size_t count, uint32_t const* names);
+ void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels);
class BindImageAsFramebuffer {
RenderEngine& mEngine;
@@ -106,8 +107,6 @@
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
-
-
EGLContext getEGLContext() const;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index def4618..3733ede 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -59,6 +59,7 @@
#include "Colorizer.h"
#include "DdmConnection.h"
#include "DisplayDevice.h"
+#include "DispSync.h"
#include "EventThread.h"
#include "Layer.h"
#include "LayerDim.h"
@@ -84,6 +85,37 @@
EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
namespace android {
+
+// This works around the lack of support for the sync framework on some
+// devices.
+#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK
+static const bool runningWithoutSyncFramework = true;
+#else
+static const bool runningWithoutSyncFramework = false;
+#endif
+
+// This is the phase offset in nanoseconds of the software vsync event
+// relative to the vsync event reported by HWComposer. The software vsync
+// event is when SurfaceFlinger and Choreographer-based applications run each
+// frame.
+//
+// This phase offset allows adjustment of the minimum latency from application
+// wake-up (by Choregographer) time to the time at which the resulting window
+// image is displayed. This value may be either positive (after the HW vsync)
+// or negative (before the HW vsync). Setting it to 0 will result in a
+// minimum latency of two vsync periods because the app and SurfaceFlinger
+// will run just after the HW vsync. Setting it to a positive number will
+// result in the minimum latency being:
+//
+// (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
+//
+// Note that reducing this latency makes it more likely for the applications
+// to not have their window content image ready in time. When this happens
+// the latency will end up being an additional vsync period, and animations
+// will hiccup. Therefore, this latency should be tuned somewhat
+// conservatively (or at least with awareness of the trade-off being made).
+static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
+
// ---------------------------------------------------------------------------
const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -114,6 +146,7 @@
mDebugInTransaction(0),
mLastTransactionTime(0),
mBootFinished(false),
+ mPrimaryHWVsyncEnabled(false),
mDaltonize(false)
{
ALOGI("SurfaceFlinger is starting");
@@ -366,56 +399,103 @@
operator EGLint const* () const { return &mList.keyAt(0).v; }
};
-EGLConfig SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisualId) {
+status_t SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisualId,
+ EGLint renderableType, EGLConfig* config) {
// select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
// it is to be used with WIFI displays
- EGLConfig config;
- EGLint dummy;
status_t err;
+ EGLint wantedAttribute;
+ EGLint wantedAttributeValue;
EGLAttributeVector attribs;
- // TODO: enable ES2
- //attribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT;
- attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
- attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
- attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
- attribs[EGL_RED_SIZE] = 8;
- attribs[EGL_GREEN_SIZE] = 8;
- attribs[EGL_BLUE_SIZE] = 8;
+ if (renderableType) {
+ attribs[EGL_RENDERABLE_TYPE] = renderableType;
+ attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
+ attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT|EGL_PBUFFER_BIT;
+ attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
+ attribs[EGL_RED_SIZE] = 8;
+ attribs[EGL_GREEN_SIZE] = 8;
+ attribs[EGL_BLUE_SIZE] = 8;
+ wantedAttribute = EGL_NONE;
+ wantedAttributeValue = EGL_NONE;
- err = selectConfigForAttribute(display, attribs, EGL_NONE, EGL_NONE, &config);
- if (!err)
- goto success;
+ } else {
+ // if no renderable type specified, fallback to a simplified query
+ wantedAttribute = EGL_NATIVE_VISUAL_ID;
+ wantedAttributeValue = nativeVisualId;
+ }
- // this didn't work, probably because we're on the emulator...
- // try a simplified query
- ALOGW("no suitable EGLConfig found, trying a simpler query");
- attribs.remove(EGL_RENDERABLE_TYPE);
- attribs.remove(EGL_FRAMEBUFFER_TARGET_ANDROID);
- attribs.remove(EGL_RECORDABLE_ANDROID);
- attribs.remove(EGL_RED_SIZE);
- attribs.remove(EGL_GREEN_SIZE);
- attribs.remove(EGL_BLUE_SIZE);
- err = selectConfigForAttribute(display, attribs,
- EGL_NATIVE_VISUAL_ID, nativeVisualId, &config);
- if (!err)
- goto success;
-
- // this EGL is too lame for Android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
- return 0;
-
-success:
- if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy))
- ALOGW_IF(dummy == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
- return config;
+ err = selectConfigForAttribute(display, attribs, wantedAttribute,
+ wantedAttributeValue, config);
+ if (err == NO_ERROR) {
+ EGLint caveat;
+ if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+ ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+ }
+ return err;
}
-void SurfaceFlinger::init() {
+class DispSyncSource : public VSyncSource, private DispSync::Callback {
+public:
+ DispSyncSource(DispSync* dispSync) : mValue(0), mDispSync(dispSync) {}
+ virtual ~DispSyncSource() {}
+
+ virtual void setVSyncEnabled(bool enable) {
+ // Do NOT lock the mutex here so as to avoid any mutex ordering issues
+ // with locking it in the onDispSyncEvent callback.
+ if (enable) {
+ status_t err = mDispSync->addEventListener(vsyncPhaseOffsetNs,
+ static_cast<DispSync::Callback*>(this));
+ if (err != NO_ERROR) {
+ ALOGE("error registering vsync callback: %s (%d)",
+ strerror(-err), err);
+ }
+ ATRACE_INT("VsyncOn", 1);
+ } else {
+ status_t err = mDispSync->removeEventListener(
+ static_cast<DispSync::Callback*>(this));
+ if (err != NO_ERROR) {
+ ALOGE("error unregistering vsync callback: %s (%d)",
+ strerror(-err), err);
+ }
+ ATRACE_INT("VsyncOn", 0);
+ }
+ }
+
+ virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+ mCallback = callback;
+ }
+
+private:
+ virtual void onDispSyncEvent(nsecs_t when) {
+ sp<VSyncSource::Callback> callback;
+ {
+ Mutex::Autolock lock(mMutex);
+ callback = mCallback;
+
+ mValue = (mValue + 1) % 2;
+ ATRACE_INT("VSYNC", mValue);
+ }
+
+ if (callback != NULL) {
+ callback->onVSyncEvent(when);
+ }
+ }
+
+ int mValue;
+
+ DispSync* mDispSync;
+ sp<VSyncSource::Callback> mCallback;
+ Mutex mMutex;
+};
+
+void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
+ status_t err;
Mutex::Autolock _l(mStateLock);
// initialize EGL for the default display
@@ -427,8 +507,27 @@
mHwc = new HWComposer(this,
*static_cast<HWComposer::EventHandler *>(this));
- // initialize the config and context (can't fail)
- mEGLConfig = selectEGLConfig(mEGLDisplay, mHwc->getVisualID());
+ // First try to get an ES2 config
+ err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), EGL_OPENGL_ES2_BIT,
+ &mEGLConfig);
+
+ if (err != NO_ERROR) {
+ // If ES2 fails, try ES1
+ err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(),
+ EGL_OPENGL_ES_BIT, &mEGLConfig);
+ }
+
+ if (err != NO_ERROR) {
+ // still didn't work, probably because we're on the emulator...
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), 0, &mEGLConfig);
+ }
+
+ if (err != NO_ERROR) {
+ // this EGL is too lame for android
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ }
// print some debugging info
EGLint r,g,b,a;
@@ -488,9 +587,15 @@
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
// start the EventThread
- mEventThread = new EventThread(this);
+ sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync);
+ mEventThread = new EventThread(vsyncSrc);
mEventQueue.setEventThread(mEventThread);
+ // set a fake vsync period if there is no HWComposer
+ if (mHwc->initCheck() != NO_ERROR) {
+ mPrimaryDispSync.setPeriod(16666667);
+ }
+
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -645,17 +750,49 @@
} while (true);
}
-void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
- if (mEventThread == NULL) {
- // This is a temporary workaround for b/7145521. A non-null pointer
- // does not mean EventThread has finished initializing, so this
- // is not a correct fix.
- ALOGW("WARNING: EventThread not started, ignoring vsync");
- return;
+void SurfaceFlinger::enableHardwareVsync() {
+ Mutex::Autolock _l(mHWVsyncLock);
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync.beginResync();
+ eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+ mPrimaryHWVsyncEnabled = true;
}
- if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- // we should only receive DisplayDevice::DisplayType from the vsync callback
- mEventThread->onVSyncReceived(type, timestamp);
+}
+
+void SurfaceFlinger::resyncToHardwareVsync() {
+ Mutex::Autolock _l(mHWVsyncLock);
+
+ const nsecs_t period =
+ getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+
+ mPrimaryDispSync.reset();
+ mPrimaryDispSync.setPeriod(period);
+
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync.beginResync();
+ eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+ mPrimaryHWVsyncEnabled = true;
+ }
+}
+
+void SurfaceFlinger::disableHardwareVsync() {
+ Mutex::Autolock _l(mHWVsyncLock);
+ if (mPrimaryHWVsyncEnabled) {
+ eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
+ mPrimaryDispSync.endResync();
+ mPrimaryHWVsyncEnabled = false;
+ }
+}
+
+void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
+ if (type == 0) {
+ bool needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
+
+ if (needsHwVsync) {
+ enableHardwareVsync();
+ } else {
+ disableHardwareVsync();
+ }
}
}
@@ -683,6 +820,7 @@
}
void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
+ ATRACE_CALL();
getHwComposer().eventControl(disp, event, enabled);
}
@@ -788,11 +926,27 @@
layers[i]->onPostComposition();
}
+ const HWComposer& hwc = getHwComposer();
+ sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+
+ if (presentFence->isValid()) {
+ if (mPrimaryDispSync.addPresentFence(presentFence)) {
+ enableHardwareVsync();
+ } else {
+ disableHardwareVsync();
+ }
+ }
+
+ if (runningWithoutSyncFramework) {
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+ if (hw->isScreenAcquired()) {
+ enableHardwareVsync();
+ }
+ }
+
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
- const HWComposer& hwc = getHwComposer();
- sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
mAnimFrameTracker.setActualPresentFence(presentFence);
} else {
@@ -949,6 +1103,12 @@
hwc.commit();
}
+ // make the default display current because the VirtualDisplayDevice code cannot
+ // deal with dequeueBuffer() being called outside of the composition loop; however
+ // the code below can call glFlush() which is allowed (and does in some case) call
+ // dequeueBuffer().
+ getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
+
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
sp<const DisplayDevice> hw(mDisplays[dpy]);
const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ());
@@ -1507,7 +1667,7 @@
HWComposer::LayerListIterator cur = hwc.begin(id);
const HWComposer::LayerListIterator end = hwc.end(id);
- const bool hasGlesComposition = hwc.hasGlesComposition(id) || (cur==end);
+ bool hasGlesComposition = hwc.hasGlesComposition(id);
if (hasGlesComposition) {
if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) {
ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -1580,9 +1740,10 @@
if (!clip.isEmpty()) {
switch (cur->getCompositionType()) {
case HWC_OVERLAY: {
+ const Layer::State& state(layer->getDrawingState());
if ((cur->getHints() & HWC_HINT_CLEAR_FB)
&& i
- && layer->isOpaque()
+ && layer->isOpaque() && (state.alpha == 0xFF)
&& hasGlesComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
@@ -2016,6 +2177,8 @@
if (type == DisplayDevice::DISPLAY_PRIMARY) {
// FIXME: eventthread only knows about the main display right now
mEventThread->onScreenAcquired();
+
+ resyncToHardwareVsync();
}
}
mVisibleRegionsDirty = true;
@@ -2818,6 +2981,15 @@
// dependent on the context's EGLConfig.
renderScreenImplLocked(hw, reqWidth, reqHeight,
minLayerZ, maxLayerZ, true);
+
+ if (DEBUG_SCREENSHOTS) {
+ uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
+ getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
+ checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
+ hw, minLayerZ, maxLayerZ);
+ delete [] pixels;
+ }
+
} else {
ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
result = INVALID_OPERATION;
@@ -2838,13 +3010,12 @@
return result;
}
-void SurfaceFlinger::checkScreenshot(const sp<GraphicBuffer>& buf, void const* vaddr,
- const sp<const DisplayDevice>& hw,
- uint32_t minLayerZ, uint32_t maxLayerZ) {
+void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
+ const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
if (DEBUG_SCREENSHOTS) {
- for (ssize_t y=0 ; y<buf->height ; y++) {
- uint32_t const * p = (uint32_t const *)vaddr + y*buf->stride;
- for (ssize_t x=0 ; x<buf->width ; x++) {
+ for (size_t y=0 ; y<h ; y++) {
+ uint32_t const * p = (uint32_t const *)vaddr + y*s;
+ for (size_t x=0 ; x<w ; x++) {
if (p[x] != 0xFF000000) return;
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 347e3e3..f1c19c2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -48,6 +48,7 @@
#include "Barrier.h"
#include "DisplayDevice.h"
+#include "DispSync.h"
#include "FrameTracker.h"
#include "MessageQueue.h"
@@ -317,7 +318,8 @@
*/
static status_t selectConfigForAttribute(EGLDisplay dpy,
EGLint const* attrs, EGLint attribute, EGLint value, EGLConfig* outConfig);
- static EGLConfig selectEGLConfig(EGLDisplay disp, EGLint visualId);
+ static status_t selectEGLConfig(EGLDisplay disp, EGLint visualId,
+ EGLint renderableType, EGLConfig* config);
size_t getMaxTextureSize() const;
size_t getMaxViewportDims() const;
@@ -377,6 +379,12 @@
* Display management
*/
+ /* ------------------------------------------------------------------------
+ * VSync
+ */
+ void enableHardwareVsync();
+ void disableHardwareVsync();
+ void resyncToHardwareVsync();
/* ------------------------------------------------------------------------
* Debugging & dumpsys
@@ -387,7 +395,7 @@
void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
bool startDdmConnection();
static void appendSfConfigString(String8& result);
- void checkScreenshot(const sp<GraphicBuffer>& buf, void const* vaddr,
+ void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
const sp<const DisplayDevice>& hw,
uint32_t minLayerZ, uint32_t maxLayerZ);
@@ -450,11 +458,16 @@
// these are thread safe
mutable MessageQueue mEventQueue;
FrameTracker mAnimFrameTracker;
+ DispSync mPrimaryDispSync;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
Vector<Layer const *> mDestroyedLayers;
+ // protected by mHWVsyncLock
+ Mutex mHWVsyncLock;
+ bool mPrimaryHWVsyncEnabled;
+
/* ------------------------------------------------------------------------
* Feature prototyping
*/