summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/binder/Android.mk3
-rw-r--r--libs/binder/AppOpsManager.cpp94
-rw-r--r--libs/binder/IAppOpsCallback.cpp70
-rw-r--r--libs/binder/IAppOpsService.cpp172
-rw-r--r--libs/binder/IMemory.cpp4
-rw-r--r--libs/binder/IPCThreadState.cpp4
-rw-r--r--libs/binder/ProcessState.cpp19
-rw-r--r--libs/diskusage/Android.mk24
-rw-r--r--libs/diskusage/MODULE_LICENSE_APACHE20
-rw-r--r--libs/diskusage/dirsize.c75
-rw-r--r--libs/gui/Android.mk46
-rw-r--r--libs/gui/BufferItemConsumer.cpp14
-rw-r--r--libs/gui/BufferQueue.cpp87
-rw-r--r--libs/gui/ConsumerBase.cpp26
-rw-r--r--libs/gui/CpuConsumer.cpp125
-rw-r--r--libs/gui/GLConsumer.cpp (renamed from libs/gui/SurfaceTexture.cpp)506
-rw-r--r--libs/gui/GraphicBufferAlloc.cpp53
-rw-r--r--libs/gui/GuiConfig.cpp10
-rw-r--r--libs/gui/IGraphicBufferProducer.cpp (renamed from libs/gui/ISurfaceTexture.cpp)118
-rw-r--r--libs/gui/ISurface.cpp66
-rw-r--r--libs/gui/ISurfaceComposer.cpp41
-rw-r--r--libs/gui/ISurfaceComposerClient.cpp56
-rw-r--r--libs/gui/LayerState.cpp54
-rw-r--r--libs/gui/Surface.cpp1013
-rw-r--r--libs/gui/SurfaceComposerClient.cpp209
-rw-r--r--libs/gui/SurfaceControl.cpp190
-rw-r--r--libs/gui/SurfaceTextureClient.cpp854
-rw-r--r--libs/gui/SyncFeatures.cpp94
-rw-r--r--libs/gui/tests/Android.mk1
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp14
-rw-r--r--libs/gui/tests/CpuConsumer_test.cpp270
-rw-r--r--libs/gui/tests/SurfaceTextureClient_test.cpp56
-rw-r--r--libs/gui/tests/SurfaceTexture_test.cpp242
-rw-r--r--libs/gui/tests/Surface_test.cpp22
-rw-r--r--libs/ui/Android.mk3
-rw-r--r--libs/ui/Fence.cpp63
-rw-r--r--libs/ui/GraphicBuffer.cpp21
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp132
-rw-r--r--libs/ui/GraphicBufferMapper.cpp14
-rw-r--r--libs/ui/Region.cpp140
-rw-r--r--libs/ui/tests/Android.mk24
-rw-r--r--libs/ui/tests/Region_test.cpp156
-rw-r--r--libs/ui/tests/region/Android.mk16
-rw-r--r--libs/ui/tests/region/region.cpp69
-rw-r--r--libs/utils/Android.mk6
-rw-r--r--libs/utils/BasicHashtable.cpp2
-rw-r--r--libs/utils/CallStack.cpp15
-rw-r--r--libs/utils/JenkinsHash.cpp64
-rw-r--r--libs/utils/LinearAllocator.cpp227
-rw-r--r--libs/utils/RefBase.cpp160
-rw-r--r--libs/utils/Threads.cpp45
-rw-r--r--libs/utils/Trace.cpp50
-rw-r--r--libs/utils/VectorImpl.cpp10
-rw-r--r--libs/utils/tests/Android.mk1
-rw-r--r--libs/utils/tests/LruCache_test.cpp291
55 files changed, 3922 insertions, 2219 deletions
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
index d449298..994d3db 100644
--- a/libs/binder/Android.mk
+++ b/libs/binder/Android.mk
@@ -14,8 +14,11 @@
# we have the common sources, plus some device-specific stuff
sources := \
+ AppOpsManager.cpp \
Binder.cpp \
BpBinder.cpp \
+ IAppOpsCallback.cpp \
+ IAppOpsService.cpp \
IInterface.cpp \
IMemory.cpp \
IPCThreadState.cpp \
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
new file mode 100644
index 0000000..7ac1b11
--- /dev/null
+++ b/libs/binder/AppOpsManager.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include <binder/AppOpsManager.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/SystemClock.h>
+
+namespace android {
+
+static String16 _appops("appops");
+
+AppOpsManager::AppOpsManager()
+{
+}
+
+sp<IAppOpsService> AppOpsManager::getService()
+{
+ int64_t startTime = 0;
+ mLock.lock();
+ sp<IAppOpsService> service = mService;
+ while (service == NULL || !service->asBinder()->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(_appops);
+ if (binder == NULL) {
+ // Wait for the app ops service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGI("Waiting for app ops service");
+ } else if ((uptimeMillis()-startTime) > 10000) {
+ ALOGW("Waiting too long for app ops service, giving up");
+ return NULL;
+ }
+ sleep(1);
+ } else {
+ service = interface_cast<IAppOpsService>(binder);
+ mService = service;
+ }
+ }
+ mLock.unlock();
+ return service;
+}
+
+int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
+{
+ sp<IAppOpsService> service = getService();
+ return service != NULL ? service->checkOperation(op, uid, callingPackage) : MODE_IGNORED;
+}
+
+int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ return service != NULL ? service->noteOperation(op, uid, callingPackage) : MODE_IGNORED;
+}
+
+int32_t AppOpsManager::startOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ return service != NULL ? service->startOperation(op, uid, callingPackage) : MODE_IGNORED;
+}
+
+void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ if (service != NULL) {
+ service->finishOperation(op, uid, callingPackage);
+ }
+}
+
+void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback) {
+ sp<IAppOpsService> service = getService();
+ if (service != NULL) {
+ service->startWatchingMode(op, packageName, callback);
+ }
+}
+
+void AppOpsManager::stopWatchingMode(const sp<IAppOpsCallback>& callback) {
+ sp<IAppOpsService> service = getService();
+ if (service != NULL) {
+ service->stopWatchingMode(callback);
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
new file mode 100644
index 0000000..e0aad23
--- /dev/null
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 LOG_TAG "AppOpsCallback"
+
+#include <binder/IAppOpsCallback.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpAppOpsCallback : public BpInterface<IAppOpsCallback>
+{
+public:
+ BpAppOpsCallback(const sp<IBinder>& impl)
+ : BpInterface<IAppOpsCallback>(impl)
+ {
+ }
+
+ virtual void opChanged(int32_t op, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor());
+ data.writeInt32(op);
+ data.writeString16(packageName);
+ remote()->transact(OP_CHANGED_TRANSACTION, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback");
+
+// ----------------------------------------------------------------------
+
+status_t BnAppOpsCallback::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case OP_CHANGED_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsCallback, data, reply);
+ int32_t op = data.readInt32();
+ String16 packageName = data.readString16();
+ opChanged(op, packageName);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
new file mode 100644
index 0000000..282b30f
--- /dev/null
+++ b/libs/binder/IAppOpsService.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 LOG_TAG "AppOpsService"
+
+#include <binder/IAppOpsService.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpAppOpsService : public BpInterface<IAppOpsService>
+{
+public:
+ BpAppOpsService(const sp<IBinder>& impl)
+ : BpInterface<IAppOpsService>(impl)
+ {
+ }
+
+ virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ return reply.readInt32();
+ }
+
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ return reply.readInt32();
+ }
+
+ virtual int32_t startOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ return reply.readInt32();
+ }
+
+ virtual void finishOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
+ }
+
+ virtual void startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(op);
+ data.writeString16(packageName);
+ data.writeStrongBinder(callback->asBinder());
+ remote()->transact(START_WATCHING_MODE_TRANSACTION, data, &reply);
+ }
+
+ virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeStrongBinder(callback->asBinder());
+ remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
+
+// ----------------------------------------------------------------------
+
+status_t BnAppOpsService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ //printf("AppOpsService received: "); data.print();
+ switch(code) {
+ case CHECK_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = checkOperation(code, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case NOTE_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = noteOperation(code, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case START_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = startOperation(code, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case FINISH_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ finishOperation(code, uid, packageName);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ case START_WATCHING_MODE_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t op = data.readInt32();
+ String16 packageName = data.readString16();
+ sp<IAppOpsCallback> callback = interface_cast<IAppOpsCallback>(data.readStrongBinder());
+ startWatchingMode(op, packageName, callback);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ case STOP_WATCHING_MODE_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ sp<IAppOpsCallback> callback = interface_cast<IAppOpsCallback>(data.readStrongBinder());
+ stopWatchingMode(callback);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index cd2451a..07cb41a 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -246,9 +246,7 @@ BpMemoryHeap::~BpMemoryHeap() {
if (VERBOSE) {
ALOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d",
binder.get(), this, mSize, mHeapId);
- CallStack stack;
- stack.update();
- stack.dump("callstack");
+ CallStack stack(LOG_TAG);
}
munmap(mBase, mSize);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6e83faa..2ffa927 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -466,6 +466,10 @@ void IPCThreadState::joinThreadPool(bool isMain)
result = executeCommand(cmd);
+ } else if (result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
+ ALOGE("talkWithDriver(fd=%d) returned unexpected error %d, aborting",
+ mProcess->mDriverFD, result);
+ abort();
}
// After executing the command, ensure that the thread is returned to the
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index d95fd6f..294e1d4 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -283,15 +283,20 @@ void ProcessState::setArgV0(const char* txt)
}
}
+String8 ProcessState::makeBinderThreadName() {
+ int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ String8 name;
+ name.appendFormat("Binder_%X", s);
+ return name;
+}
+
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
- int32_t s = android_atomic_add(1, &mThreadPoolSeq);
- char buf[16];
- snprintf(buf, sizeof(buf), "Binder_%X", s);
- ALOGV("Spawning new pooled thread, name=%s\n", buf);
+ String8 name = makeBinderThreadName();
+ ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = new PoolThread(isMain);
- t->run(buf);
+ t->run(name.string());
}
}
@@ -304,6 +309,10 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
return result;
}
+void ProcessState::giveThreadPoolName() {
+ androidSetThreadName( makeBinderThreadName().string() );
+}
+
static int open_driver()
{
int fd = open("/dev/binder", O_RDWR);
diff --git a/libs/diskusage/Android.mk b/libs/diskusage/Android.mk
new file mode 100644
index 0000000..d54f8ad
--- /dev/null
+++ b/libs/diskusage/Android.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2010 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdiskusage
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := dirsize.c
+
+include $(BUILD_STATIC_LIBRARY) \ No newline at end of file
diff --git a/libs/diskusage/MODULE_LICENSE_APACHE2 b/libs/diskusage/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/diskusage/MODULE_LICENSE_APACHE2
diff --git a/libs/diskusage/dirsize.c b/libs/diskusage/dirsize.c
new file mode 100644
index 0000000..24e5af0
--- /dev/null
+++ b/libs/diskusage/dirsize.c
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright (C) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <diskusage/dirsize.h>
+
+int64_t stat_size(struct stat *s)
+{
+ int64_t blksize = s->st_blksize;
+ // count actual blocks used instead of nominal file size
+ int64_t size = s->st_blocks * 512;
+
+ if (blksize) {
+ /* round up to filesystem block size */
+ size = (size + blksize - 1) & (~(blksize - 1));
+ }
+
+ return size;
+}
+
+int64_t calculate_dir_size(int dfd)
+{
+ int64_t size = 0;
+ struct stat s;
+ DIR *d;
+ struct dirent *de;
+
+ d = fdopendir(dfd);
+ if (d == NULL) {
+ close(dfd);
+ return 0;
+ }
+
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size += stat_size(&s);
+ }
+ if (de->d_type == DT_DIR) {
+ int subfd;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0)
+ continue;
+ if ((name[1] == '.') && (name[2] == 0))
+ continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd >= 0) {
+ size += calculate_dir_size(subfd);
+ }
+ }
+ }
+ closedir(d);
+ return size;
+}
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index d970a33..c080f47 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -3,29 +3,30 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
BitTube.cpp \
+ BufferItemConsumer.cpp \
BufferQueue.cpp \
ConsumerBase.cpp \
+ CpuConsumer.cpp \
DisplayEventReceiver.cpp \
+ DummyConsumer.cpp \
+ GLConsumer.cpp \
+ GraphicBufferAlloc.cpp \
+ GuiConfig.cpp \
IDisplayEventConnection.cpp \
+ IGraphicBufferAlloc.cpp \
+ IGraphicBufferProducer.cpp \
ISensorEventConnection.cpp \
ISensorServer.cpp \
- ISurfaceTexture.cpp \
- Sensor.cpp \
- SensorEventQueue.cpp \
- SensorManager.cpp \
- SurfaceTexture.cpp \
- SurfaceTextureClient.cpp \
ISurfaceComposer.cpp \
- ISurface.cpp \
ISurfaceComposerClient.cpp \
- IGraphicBufferAlloc.cpp \
LayerState.cpp \
+ Sensor.cpp \
+ SensorEventQueue.cpp \
+ SensorManager.cpp \
Surface.cpp \
+ SurfaceControl.cpp \
SurfaceComposerClient.cpp \
- DummyConsumer.cpp \
- CpuConsumer.cpp \
- BufferItemConsumer.cpp \
- GuiConfig.cpp
+ SyncFeatures.cpp \
LOCAL_SHARED_LIBRARIES := \
libbinder \
@@ -35,27 +36,16 @@ LOCAL_SHARED_LIBRARIES := \
libsync \
libui \
libutils \
+ liblog
LOCAL_MODULE:= libgui
-ifeq ($(TARGET_BOARD_PLATFORM), omap4)
- LOCAL_CFLAGS += -DUSE_FENCE_SYNC
+ifeq ($(TARGET_BOARD_PLATFORM), tegra)
+ LOCAL_CFLAGS += -DDONT_USE_FENCE_SYNC
endif
-ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
- LOCAL_CFLAGS += -DUSE_FENCE_SYNC
-endif
-ifeq ($(TARGET_BOARD_PLATFORM), exynos5)
- LOCAL_CFLAGS += -DUSE_NATIVE_FENCE_SYNC
- LOCAL_CFLAGS += -DUSE_WAIT_SYNC
-endif
-ifneq ($(filter generic%,$(TARGET_DEVICE)),)
- # Emulator build
- LOCAL_CFLAGS += -DUSE_FENCE_SYNC
-endif
-
-ifeq ($(TARGET_BOARD_PLATFORM), msm8960)
- LOCAL_CFLAGS += -DUSE_NATIVE_FENCE_SYNC
+ifeq ($(TARGET_BOARD_PLATFORM), tegra3)
+ LOCAL_CFLAGS += -DDONT_USE_FENCE_SYNC
endif
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 5079883..7db1b84 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -62,8 +62,8 @@ status_t BufferItemConsumer::acquireBuffer(BufferItem *item, bool waitForFence)
return err;
}
- if (waitForFence && item->mFence.get()) {
- err = item->mFence->waitForever(1000, "BufferItemConsumer::acquireBuffer");
+ if (waitForFence) {
+ err = item->mFence->waitForever("BufferItemConsumer::acquireBuffer");
if (err != OK) {
BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)",
strerror(-err), err);
@@ -93,4 +93,14 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
return err;
}
+status_t BufferItemConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mMutex);
+ return mBufferQueue->setDefaultBufferSize(w, h);
+}
+
+status_t BufferItemConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
+ Mutex::Autolock _l(mMutex);
+ return mBufferQueue->setDefaultBufferFormat(defaultFormat);
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 086e298..b4c7231 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -29,7 +29,6 @@
#include <private/gui/ComposerService.h>
#include <utils/Log.h>
-#include <gui/SurfaceTexture.h>
#include <utils/Trace.h>
// Macros for including the BufferQueue name in log messages
@@ -107,7 +106,7 @@ status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) {
mDefaultMaxBufferCount = count;
mDequeueCondition.broadcast();
- return OK;
+ return NO_ERROR;
}
bool BufferQueue::isSynchronousMode() const {
@@ -123,20 +122,20 @@ void BufferQueue::setConsumerName(const String8& name) {
status_t BufferQueue::setDefaultBufferFormat(uint32_t defaultFormat) {
Mutex::Autolock lock(mMutex);
mDefaultBufferFormat = defaultFormat;
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::setConsumerUsageBits(uint32_t usage) {
Mutex::Autolock lock(mMutex);
mConsumerUsageBits = usage;
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::setTransformHint(uint32_t hint) {
ST_LOGV("setTransformHint: %02x", hint);
Mutex::Autolock lock(mMutex);
mTransformHint = hint;
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::setBufferCount(int bufferCount) {
@@ -147,11 +146,12 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
+ ST_LOGE("setBufferCount: BufferQueue has been abandoned!");
return NO_INIT;
}
if (bufferCount > NUM_BUFFER_SLOTS) {
- ST_LOGE("setBufferCount: bufferCount larger than slots available");
+ ST_LOGE("setBufferCount: bufferCount too large (max %d)",
+ NUM_BUFFER_SLOTS);
return BAD_VALUE;
}
@@ -168,7 +168,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
if (bufferCount == 0) {
mOverrideMaxBufferCount = 0;
mDequeueCondition.broadcast();
- return OK;
+ return NO_ERROR;
}
if (bufferCount < minBufferSlots) {
@@ -192,7 +192,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
listener->onBuffersReleased();
}
- return OK;
+ return NO_ERROR;
}
int BufferQueue::query(int what, int* outValue)
@@ -201,7 +201,7 @@ int BufferQueue::query(int what, int* outValue)
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("query: SurfaceTexture has been abandoned!");
+ ST_LOGE("query: BufferQueue has been abandoned!");
return NO_INIT;
}
@@ -234,7 +234,7 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ST_LOGV("requestBuffer: slot=%d", slot);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("requestBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
int maxBufferCount = getMaxBufferCountLocked();
@@ -255,7 +255,7 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
return NO_ERROR;
}
-status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
+status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
ATRACE_CALL();
ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
@@ -283,7 +283,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
bool tryAgain = true;
while (tryAgain) {
if (mAbandoned) {
- ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
@@ -295,7 +295,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
assert(mSlots[i].mBufferState == BufferSlot::FREE);
if (mSlots[i].mGraphicBuffer != NULL) {
freeBufferLocked(i);
- returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+ returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS;
}
}
@@ -373,8 +373,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
h = mDefaultHeight;
}
- // buffer is now in DEQUEUED (but can also be current at the same time,
- // if we're in synchronous mode)
mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
@@ -388,20 +386,20 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
mSlots[buf].mGraphicBuffer = NULL;
mSlots[buf].mRequestBufferCalled = false;
mSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
- mSlots[buf].mFence.clear();
+ mSlots[buf].mFence = Fence::NO_FENCE;
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
- returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
}
dpy = mSlots[buf].mEglDisplay;
eglFence = mSlots[buf].mEglFence;
- outFence = mSlots[buf].mFence;
+ *outFence = mSlots[buf].mFence;
mSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
- mSlots[buf].mFence.clear();
+ mSlots[buf].mFence = Fence::NO_FENCE;
} // end lock scope
- if (returnFlags & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) {
+ if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
status_t error;
sp<GraphicBuffer> graphicBuffer(
mGraphicBufferAlloc->createGraphicBuffer(
@@ -416,7 +414,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
@@ -424,7 +422,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
}
}
-
if (eglFence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(dpy, eglFence, 0, 1000000000);
// If something goes wrong, log the error, but return the buffer without
@@ -450,7 +447,7 @@ status_t BufferQueue::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+ ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!");
return NO_INIT;
}
@@ -489,6 +486,11 @@ status_t BufferQueue::queueBuffer(int buf,
input.deflate(&timestamp, &crop, &scalingMode, &transform, &fence);
+ if (fence == NULL) {
+ ST_LOGE("queueBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x "
"scale=%s",
buf, timestamp, crop.left, crop.top, crop.right, crop.bottom,
@@ -499,7 +501,7 @@ status_t BufferQueue::queueBuffer(int buf,
{ // scope for the lock
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("queueBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
int maxBufferCount = getMaxBufferCountLocked();
@@ -586,10 +588,10 @@ status_t BufferQueue::queueBuffer(int buf,
if (listener != 0) {
listener->onFrameAvailable();
}
- return OK;
+ return NO_ERROR;
}
-void BufferQueue::cancelBuffer(int buf, sp<Fence> fence) {
+void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) {
ATRACE_CALL();
ST_LOGV("cancelBuffer: slot=%d", buf);
Mutex::Autolock lock(mMutex);
@@ -608,6 +610,9 @@ void BufferQueue::cancelBuffer(int buf, sp<Fence> fence) {
ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
buf, mSlots[buf].mBufferState);
return;
+ } else if (fence == NULL) {
+ ST_LOGE("cancelBuffer: fence is NULL");
+ return;
}
mSlots[buf].mBufferState = BufferSlot::FREE;
mSlots[buf].mFrameNumber = 0;
@@ -786,7 +791,7 @@ void BufferQueue::freeBufferLocked(int slot) {
eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence);
mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
}
- mSlots[slot].mFence.clear();
+ mSlots[slot].mFence = Fence::NO_FENCE;
}
void BufferQueue::freeAllBuffersLocked() {
@@ -844,7 +849,7 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) {
mSlots[buf].mAcquireCalled = true;
mSlots[buf].mNeedsCleanupOnRelease = false;
mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
- mSlots[buf].mFence.clear();
+ mSlots[buf].mFence = Fence::NO_FENCE;
mQueue.erase(front);
mDequeueCondition.broadcast();
@@ -854,7 +859,7 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) {
return NO_BUFFER_AVAILABLE;
}
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
@@ -864,8 +869,8 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
Mutex::Autolock _l(mMutex);
- if (buf == INVALID_BUFFER_SLOT) {
- return -EINVAL;
+ if (buf == INVALID_BUFFER_SLOT || fence == NULL) {
+ return BAD_VALUE;
}
mSlots[buf].mEglDisplay = display;
@@ -885,7 +890,7 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
}
mDequeueCondition.broadcast();
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) {
@@ -896,10 +901,14 @@ status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListen
ST_LOGE("consumerConnect: BufferQueue has been abandoned!");
return NO_INIT;
}
+ if (consumerListener == NULL) {
+ ST_LOGE("consumerConnect: consumerListener may not be NULL");
+ return BAD_VALUE;
+ }
mConsumerListener = consumerListener;
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::consumerDisconnect() {
@@ -916,7 +925,7 @@ status_t BufferQueue::consumerDisconnect() {
mQueue.clear();
freeAllBuffersLocked();
mDequeueCondition.broadcast();
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) {
@@ -952,7 +961,7 @@ status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h)
Mutex::Autolock lock(mMutex);
mDefaultWidth = w;
mDefaultHeight = h;
- return OK;
+ return NO_ERROR;
}
status_t BufferQueue::setDefaultMaxBufferCount(int bufferCount) {
@@ -973,7 +982,7 @@ status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
return INVALID_OPERATION;
}
mMaxAcquiredBufferCount = maxAcquiredBuffers;
- return OK;
+ return NO_ERROR;
}
void BufferQueue::freeAllBuffersExceptHeadLocked() {
@@ -991,7 +1000,7 @@ void BufferQueue::freeAllBuffersExceptHeadLocked() {
}
status_t BufferQueue::drainQueueLocked() {
- while (mSynchronousMode && !mQueue.isEmpty()) {
+ while (mSynchronousMode && mQueue.size() > 1) {
mDequeueCondition.wait(mMutex);
if (mAbandoned) {
ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!");
@@ -1008,7 +1017,7 @@ status_t BufferQueue::drainQueueLocked() {
status_t BufferQueue::drainQueueAndFreeBuffersLocked() {
status_t err = drainQueueLocked();
if (err == NO_ERROR) {
- if (mSynchronousMode) {
+ if (mQueue.empty()) {
freeAllBuffersLocked();
} else {
freeAllBuffersExceptHeadLocked();
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 624d7e0..4937b17 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -18,7 +18,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
-#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/egl.h>
@@ -69,7 +68,7 @@ ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
status_t err = mBufferQueue->consumerConnect(proxy);
if (err != NO_ERROR) {
- CB_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)",
+ CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
} else {
mBufferQueue->setConsumerName(mName);
@@ -77,14 +76,25 @@ ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
}
ConsumerBase::~ConsumerBase() {
- CB_LOGV("~ConsumerBase");
+ CB_LOGV("~ConsumerBase");
+ Mutex::Autolock lock(mMutex);
+
+ // Verify that abandon() has been called before we get here. This should
+ // be done by ConsumerBase::onLastStrongRef(), but it's possible for a
+ // derived class to override that method and not call
+ // ConsumerBase::onLastStrongRef().
+ LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the "
+ "consumer is not abandoned!", mName.string());
+}
+
+void ConsumerBase::onLastStrongRef(const void* id) {
abandon();
}
void ConsumerBase::freeBufferLocked(int slotIndex) {
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mSlots[slotIndex].mGraphicBuffer = 0;
- mSlots[slotIndex].mFence = 0;
+ mSlots[slotIndex].mFence = Fence::NO_FENCE;
}
// Used for refactoring, should not be in final interface
@@ -99,7 +109,7 @@ void ConsumerBase::onFrameAvailable() {
sp<FrameAvailableListener> listener;
{ // scope for the lock
Mutex::Autolock lock(mMutex);
- listener = mFrameAvailableListener;
+ listener = mFrameAvailableListener.promote();
}
if (listener != NULL) {
@@ -148,7 +158,7 @@ void ConsumerBase::abandonLocked() {
}
void ConsumerBase::setFrameAvailableListener(
- const sp<FrameAvailableListener>& listener) {
+ const wp<FrameAvailableListener>& listener) {
CB_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
@@ -228,9 +238,9 @@ status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display,
freeBufferLocked(slot);
}
- mSlots[slot].mFence.clear();
+ mSlots[slot].mFence = Fence::NO_FENCE;
return err;
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 710e1af..0543649 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -17,8 +17,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "CpuConsumer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <utils/Log.h>
+#include <cutils/compiler.h>
+#include <utils/Log.h>
#include <gui/CpuConsumer.h>
#define CC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
@@ -29,24 +30,25 @@
namespace android {
-CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers) :
+CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers, bool synchronousMode) :
ConsumerBase(new BufferQueue(true) ),
mMaxLockedBuffers(maxLockedBuffers),
mCurrentLockedBuffers(0)
{
+ // Create tracking entries for locked buffers
+ mAcquiredBuffers.insertAt(0, maxLockedBuffers);
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mBufferPointers[i] = NULL;
- }
-
- mBufferQueue->setSynchronousMode(true);
+ mBufferQueue->setSynchronousMode(synchronousMode);
mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN);
mBufferQueue->setMaxAcquiredBufferCount(maxLockedBuffers);
}
CpuConsumer::~CpuConsumer() {
+ // ConsumerBase destructor does all the work.
}
+
+
void CpuConsumer::setName(const String8& name) {
Mutex::Autolock _l(mMutex);
mName = name;
@@ -78,7 +80,7 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
int buf = b.mBuf;
if (b.mFence.get()) {
- err = b.mFence->waitForever(1000, "CpuConsumer::lockNextBuffer");
+ err = b.mFence->waitForever("CpuConsumer::lockNextBuffer");
if (err != OK) {
CC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)",
strerror(-err), err);
@@ -86,22 +88,57 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
}
}
- err = mSlots[buf].mGraphicBuffer->lock(
- GraphicBuffer::USAGE_SW_READ_OFTEN,
- b.mCrop,
- &mBufferPointers[buf]);
+ void *bufferPointer = NULL;
+ android_ycbcr ycbcr = android_ycbcr();
- if (mBufferPointers[buf] != NULL && err != OK) {
- CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err),
- err);
- return err;
+ if (mSlots[buf].mGraphicBuffer->getPixelFormat() ==
+ HAL_PIXEL_FORMAT_YCbCr_420_888) {
+ err = mSlots[buf].mGraphicBuffer->lockYCbCr(
+ GraphicBuffer::USAGE_SW_READ_OFTEN,
+ b.mCrop,
+ &ycbcr);
+
+ if (err != OK) {
+ CC_LOGE("Unable to lock YCbCr buffer for CPU reading: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ bufferPointer = ycbcr.y;
+ } else {
+ err = mSlots[buf].mGraphicBuffer->lock(
+ GraphicBuffer::USAGE_SW_READ_OFTEN,
+ b.mCrop,
+ &bufferPointer);
+
+ if (err != OK) {
+ CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ }
+
+ size_t lockedIdx = 0;
+ for (; lockedIdx < mMaxLockedBuffers; lockedIdx++) {
+ if (mAcquiredBuffers[lockedIdx].mSlot ==
+ BufferQueue::INVALID_BUFFER_SLOT) {
+ break;
+ }
}
+ assert(lockedIdx < mMaxLockedBuffers);
- nativeBuffer->data = reinterpret_cast<uint8_t*>(mBufferPointers[buf]);
+ AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx);
+ ab.mSlot = buf;
+ ab.mBufferPointer = bufferPointer;
+ ab.mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+
+ nativeBuffer->data =
+ reinterpret_cast<uint8_t*>(bufferPointer);
nativeBuffer->width = mSlots[buf].mGraphicBuffer->getWidth();
nativeBuffer->height = mSlots[buf].mGraphicBuffer->getHeight();
nativeBuffer->format = mSlots[buf].mGraphicBuffer->getPixelFormat();
- nativeBuffer->stride = mSlots[buf].mGraphicBuffer->getStride();
+ nativeBuffer->stride = (ycbcr.y != NULL) ?
+ ycbcr.ystride :
+ mSlots[buf].mGraphicBuffer->getStride();
nativeBuffer->crop = b.mCrop;
nativeBuffer->transform = b.mTransform;
@@ -109,6 +146,11 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
nativeBuffer->timestamp = b.mTimestamp;
nativeBuffer->frameNumber = b.mFrameNumber;
+ nativeBuffer->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb);
+ nativeBuffer->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr);
+ nativeBuffer->chromaStride = ycbcr.cstride;
+ nativeBuffer->chromaStep = ycbcr.chroma_step;
+
mCurrentLockedBuffers++;
return OK;
@@ -116,43 +158,50 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) {
Mutex::Autolock _l(mMutex);
- int slotIndex = 0;
+ size_t lockedIdx = 0;
status_t err;
void *bufPtr = reinterpret_cast<void *>(nativeBuffer.data);
- for (; slotIndex < BufferQueue::NUM_BUFFER_SLOTS; slotIndex++) {
- if (bufPtr == mBufferPointers[slotIndex]) break;
+ for (; lockedIdx < mMaxLockedBuffers; lockedIdx++) {
+ if (bufPtr == mAcquiredBuffers[lockedIdx].mBufferPointer) break;
}
- if (slotIndex == BufferQueue::NUM_BUFFER_SLOTS) {
+ if (lockedIdx == mMaxLockedBuffers) {
CC_LOGE("%s: Can't find buffer to free", __FUNCTION__);
return BAD_VALUE;
}
- mBufferPointers[slotIndex] = NULL;
- err = mSlots[slotIndex].mGraphicBuffer->unlock();
+ return releaseAcquiredBufferLocked(lockedIdx);
+}
+
+status_t CpuConsumer::releaseAcquiredBufferLocked(int lockedIdx) {
+ status_t err;
+
+ err = mAcquiredBuffers[lockedIdx].mGraphicBuffer->unlock();
if (err != OK) {
- CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, slotIndex);
+ CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__,
+ lockedIdx);
return err;
}
- releaseBufferLocked(slotIndex, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ int buf = mAcquiredBuffers[lockedIdx].mSlot;
+
+ // release the buffer if it hasn't already been freed by the BufferQueue.
+ // This can happen, for example, when the producer of this buffer
+ // disconnected after this buffer was acquired.
+ if (CC_LIKELY(mAcquiredBuffers[lockedIdx].mGraphicBuffer ==
+ mSlots[buf].mGraphicBuffer)) {
+ releaseBufferLocked(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ }
- mCurrentLockedBuffers--;
+ AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx);
+ ab.mSlot = BufferQueue::INVALID_BUFFER_SLOT;
+ ab.mBufferPointer = NULL;
+ ab.mGraphicBuffer.clear();
+ mCurrentLockedBuffers--;
return OK;
}
void CpuConsumer::freeBufferLocked(int slotIndex) {
- if (mBufferPointers[slotIndex] != NULL) {
- status_t err;
- CC_LOGW("Buffer %d freed while locked by consumer", slotIndex);
- mBufferPointers[slotIndex] = NULL;
- err = mSlots[slotIndex].mGraphicBuffer->unlock();
- if (err != OK) {
- CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__,
- slotIndex);
- }
- mCurrentLockedBuffers--;
- }
ConsumerBase::freeBufferLocked(slotIndex);
}
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/GLConsumer.cpp
index b4dfb5e..dc46a51 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceTexture"
+#define LOG_TAG "GLConsumer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -28,50 +28,27 @@
#include <hardware/hardware.h>
+#include <gui/GLConsumer.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceTexture.h>
#include <private/gui/ComposerService.h>
+#include <private/gui/SyncFeatures.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-// This compile option makes SurfaceTexture use the
-// EGL_ANDROID_native_fence_sync extension to create Android native fences to
-// signal when all GLES reads for a given buffer have completed. It is not
-// compatible with using the EGL_KHR_fence_sync extension for the same
-// purpose.
-#ifdef USE_NATIVE_FENCE_SYNC
-#ifdef USE_FENCE_SYNC
-#error "USE_NATIVE_FENCE_SYNC and USE_FENCE_SYNC are incompatible"
-#endif
-static const bool useNativeFenceSync = true;
-#else
-static const bool useNativeFenceSync = false;
-#endif
-
-// This compile option makes SurfaceTexture use the EGL_ANDROID_sync_wait
-// extension to insert server-side waits into the GLES command stream. This
-// feature requires the EGL_ANDROID_native_fence_sync and
-// EGL_ANDROID_wait_sync extensions.
-#ifdef USE_WAIT_SYNC
-static const bool useWaitSync = true;
-#else
-static const bool useWaitSync = false;
-#endif
-
-// Macros for including the SurfaceTexture name in log messages
+namespace android {
+
+// Macros for including the GLConsumer name in log messages
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
-namespace android {
-
// Transform matrices
static float mtxIdentity[16] = {
1, 0, 0, 0,
@@ -97,41 +74,36 @@ static float mtxRot90[16] = {
0, 0, 1, 0,
1, 0, 0, 1,
};
-static float mtxRot180[16] = {
- -1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, 1, 0,
- 1, 1, 0, 1,
-};
-static float mtxRot270[16] = {
- 0, -1, 0, 0,
- 1, 0, 0, 0,
- 0, 0, 1, 0,
- 0, 1, 0, 1,
-};
static void mtxMul(float out[16], const float a[16], const float b[16]);
-SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
+GLConsumer::GLConsumer(const sp<BufferQueue>& bq, GLuint tex,
+ GLenum texTarget, bool useFenceSync) :
+ ConsumerBase(bq),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget) {}
+
+
+GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
mFilteringEnabled(true),
mTexName(tex),
-#ifdef USE_FENCE_SYNC
mUseFenceSync(useFenceSync),
-#else
- mUseFenceSync(false),
-#endif
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true)
{
- ST_LOGV("SurfaceTexture");
+ ST_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity,
sizeof(mCurrentTransformMatrix));
@@ -139,13 +111,13 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
-status_t SurfaceTexture::setDefaultMaxBufferCount(int bufferCount) {
+status_t GLConsumer::setDefaultMaxBufferCount(int bufferCount) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setDefaultMaxBufferCount(bufferCount);
}
-status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
+status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h)
{
Mutex::Autolock lock(mMutex);
mDefaultWidth = w;
@@ -153,11 +125,54 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
return mBufferQueue->setDefaultBufferSize(w, h);
}
-status_t SurfaceTexture::updateTexImage() {
- return SurfaceTexture::updateTexImage(NULL, false);
+status_t GLConsumer::updateTexImage() {
+ ATRACE_CALL();
+ ST_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("updateTexImage: GLConsumer is abandoned!");
+ return NO_INIT;
+ }
+
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferQueue::BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = acquireBufferLocked(&item);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ ST_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(mTexTarget, mTexName);
+ err = NO_ERROR;
+ } else {
+ ST_LOGE("updateTexImage: acquire failed: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = releaseAndUpdateLocked(item);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(mTexTarget, mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture, and wait until it's ready.
+ return bindTextureImageLocked();
}
-status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
+status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) {
status_t err = ConsumerBase::acquireBufferLocked(item);
if (err != NO_ERROR) {
return err;
@@ -165,186 +180,187 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
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) {
- eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
+ if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
+ ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
+ slot);
+ // keep going
+ }
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
}
}
- // Update the GL texture object. We may have to do this even when
- // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
- // detaching from a context but the buffer has not been re-allocated.
- if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) {
- EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
- if (image == EGL_NO_IMAGE_KHR) {
- return UNKNOWN_ERROR;
- }
- mEglSlots[slot].mEglImage = image;
- }
-
return NO_ERROR;
}
-status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
+status_t GLConsumer::releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence) {
- status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
- eglFence);
+ status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence);
mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
return err;
}
-status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) {
- ATRACE_CALL();
- ST_LOGV("updateTexImage");
- Mutex::Autolock lock(mMutex);
-
+status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
+{
status_t err = NO_ERROR;
- if (mAbandoned) {
- ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
- return NO_INIT;
- }
-
if (!mAttached) {
- ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL "
+ ST_LOGE("releaseAndUpdate: GLConsumer is not attached to an OpenGL "
"ES context");
return INVALID_OPERATION;
}
- EGLDisplay dpy = eglGetCurrentDisplay();
- EGLContext ctx = eglGetCurrentContext();
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
- if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
- dpy == EGL_NO_DISPLAY) {
- ST_LOGE("updateTexImage: invalid current EGLDisplay");
- return INVALID_OPERATION;
+ int buf = item.mBuf;
+
+ // If the mEglSlot entry is empty, create an EGLImage for the gralloc
+ // buffer currently in the slot in ConsumerBase.
+ //
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired), if we destroyed the
+ // 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);
+ if (image == EGL_NO_IMAGE_KHR) {
+ ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d",
+ mEglDisplay, buf);
+ return UNKNOWN_ERROR;
+ }
+ mEglSlots[buf].mEglImage = image;
}
- if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
- ctx == EGL_NO_CONTEXT) {
- ST_LOGE("updateTexImage: invalid current EGLContext");
- return INVALID_OPERATION;
+ // Do whatever sync ops we need to do before releasing the old slot.
+ err = syncForReleaseLocked(mEglDisplay);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
}
- mEglDisplay = dpy;
- mEglContext = ctx;
+ ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)",
+ mCurrentTexture,
+ mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
+ buf, mSlots[buf].mGraphicBuffer->handle);
- BufferQueue::BufferItem item;
-
- // In asynchronous mode the list is guaranteed to be one buffer
- // deep, while in synchronous mode we use the oldest buffer.
- err = acquireBufferLocked(&item);
- if (err == NO_ERROR) {
- int buf = item.mBuf;
-
- // we call the rejecter here, in case the caller has a reason to
- // not accept this buffer. this is used by SurfaceFlinger to
- // reject buffers which have the wrong size
- if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
- releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR);
- glBindTexture(mTexTarget, mTexName);
- return NO_ERROR;
+ // release old buffer
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay,
+ mEglSlots[mCurrentTexture].mEglFence);
+ if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
+ ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)",
+ strerror(-status), status);
+ err = status;
+ // keep going, with error raised [?]
}
+ }
- GLint error;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
- }
+ // Update the GLConsumer state.
+ mCurrentTexture = buf;
+ mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+ mCurrentCrop = item.mCrop;
+ mCurrentTransform = item.mTransform;
+ mCurrentScalingMode = item.mScalingMode;
+ mCurrentTimestamp = item.mTimestamp;
+ mCurrentFence = item.mFence;
- EGLImageKHR image = mEglSlots[buf].mEglImage;
- glBindTexture(mTexTarget, mTexName);
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+ computeCurrentTransformMatrixLocked();
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("updateTexImage: error binding external texture image %p "
- "(slot %d): %#04x", image, buf, error);
- err = UNKNOWN_ERROR;
- }
+ return err;
+}
- if (err == NO_ERROR) {
- err = syncForReleaseLocked(dpy);
- }
+status_t GLConsumer::bindTextureImageLocked() {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+ GLint error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(mTexTarget, mTexName);
+ if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ if (mCurrentTextureBuf == NULL) {
+ ST_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+ status_t err = bindUnslottedBufferLocked(mEglDisplay);
if (err != NO_ERROR) {
- // Release the buffer we just acquired. It's not safe to
- // release the old buffer, so instead we just drop the new frame.
- releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR);
return err;
}
+ } else {
+ EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
- ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
- mCurrentTexture,
- mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
- buf, mSlots[buf].mGraphicBuffer->handle);
-
- // release old buffer
- if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- status_t status = releaseBufferLocked(mCurrentTexture, dpy,
- mEglSlots[mCurrentTexture].mEglFence);
- if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
- ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
- strerror(-status), status);
- err = status;
- }
- }
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
- // Update the SurfaceTexture state.
- mCurrentTexture = buf;
- mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
- mCurrentCrop = item.mCrop;
- mCurrentTransform = item.mTransform;
- mCurrentScalingMode = item.mScalingMode;
- mCurrentTimestamp = item.mTimestamp;
- mCurrentFence = item.mFence;
- if (!skipSync) {
- // SurfaceFlinger needs to lazily perform GLES synchronization
- // only when it's actually going to use GLES for compositing.
- // Eventually SurfaceFlinger should have its own consumer class,
- // but for now we'll just hack it in to SurfaceTexture.
- // SurfaceFlinger is responsible for calling doGLFenceWait before
- // texturing from this SurfaceTexture.
- doGLFenceWaitLocked();
- }
- computeCurrentTransformMatrixLocked();
- } else {
- if (err < 0) {
- ST_LOGE("updateTexImage: acquire failed: %s (%d)",
- strerror(-err), err);
- return err;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGE("bindTextureImage: error binding external texture image %p"
+ ": %#04x", image, error);
+ return UNKNOWN_ERROR;
}
- // We always bind the texture even if we don't update its contents.
- glBindTexture(mTexTarget, mTexName);
- return OK;
}
- return err;
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked();
+
}
-void SurfaceTexture::setReleaseFence(int fenceFd) {
- sp<Fence> fence(new Fence(fenceFd));
- if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
- return;
- status_t err = addReleaseFence(mCurrentTexture, fence);
- if (err != OK) {
- ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
- strerror(-err), err);
+status_t GLConsumer::checkAndUpdateEglStateLocked() {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
+ dpy == EGL_NO_DISPLAY) {
+ ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
+ ctx == EGL_NO_CONTEXT) {
+ ST_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ return NO_ERROR;
+}
+
+void GLConsumer::setReleaseFence(const sp<Fence>& fence) {
+ if (fence->isValid() &&
+ mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ status_t err = addReleaseFence(mCurrentTexture, fence);
+ if (err != OK) {
+ ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
+ strerror(-err), err);
+ }
}
}
-status_t SurfaceTexture::detachFromContext() {
+status_t GLConsumer::detachFromContext() {
ATRACE_CALL();
ST_LOGV("detachFromContext");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("detachFromContext: abandoned SurfaceTexture");
+ ST_LOGE("detachFromContext: abandoned GLConsumer");
return NO_INIT;
}
if (!mAttached) {
- ST_LOGE("detachFromContext: SurfaceTexture is not attached to a "
+ ST_LOGE("detachFromContext: GLConsumer is not attached to a "
"context");
return INVALID_OPERATION;
}
@@ -373,7 +389,7 @@ status_t SurfaceTexture::detachFromContext() {
// Because we're giving up the EGLDisplay we need to free all the EGLImages
// that are associated with it. They'll be recreated when the
- // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
+ // GLConsumer gets attached to a new OpenGL ES context (and thus gets a
// new EGLDisplay).
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
EGLImageKHR img = mEglSlots[i].mEglImage;
@@ -390,18 +406,18 @@ status_t SurfaceTexture::detachFromContext() {
return OK;
}
-status_t SurfaceTexture::attachToContext(GLuint tex) {
+status_t GLConsumer::attachToContext(GLuint tex) {
ATRACE_CALL();
ST_LOGV("attachToContext");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("attachToContext: abandoned SurfaceTexture");
+ ST_LOGE("attachToContext: abandoned GLConsumer");
return NO_INIT;
}
if (mAttached) {
- ST_LOGE("attachToContext: SurfaceTexture is already attached to a "
+ ST_LOGE("attachToContext: GLConsumer is already attached to a "
"context");
return INVALID_OPERATION;
}
@@ -425,32 +441,10 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
if (mCurrentTextureBuf != NULL) {
// The EGLImageKHR that was associated with the slot was destroyed when
- // the SurfaceTexture was detached from the old context, so we need to
+ // the GLConsumer was detached from the old context, so we need to
// recreate it here.
- EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
- if (image == EGL_NO_IMAGE_KHR) {
- return UNKNOWN_ERROR;
- }
-
- // Attach the current buffer to the GL texture.
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
- GLint error;
- status_t err = OK;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("attachToContext: error binding external texture image %p "
- "(slot %d): %#04x", image, mCurrentTexture, error);
- err = UNKNOWN_ERROR;
- }
-
- // We destroy the EGLImageKHR here because the current buffer may no
- // longer be associated with one of the buffer slots, so we have
- // nowhere to to store it. If the buffer is still associated with a
- // slot then another EGLImageKHR will be created next time that buffer
- // gets acquired in updateTexImage.
- eglDestroyImageKHR(dpy, image);
-
- if (err != OK) {
+ status_t err = bindUnslottedBufferLocked(dpy);
+ if (err != NO_ERROR) {
return err;
}
}
@@ -463,11 +457,43 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
return OK;
}
-status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
+status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) {
+ ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p",
+ mCurrentTexture, mCurrentTextureBuf.get());
+
+ // Create a temporary EGLImageKHR.
+ EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
+ if (image == EGL_NO_IMAGE_KHR) {
+ return UNKNOWN_ERROR;
+ }
+
+ // Attach the current buffer to the GL texture.
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+
+ GLint error;
+ status_t err = OK;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
+ "(slot %d): %#04x", image, mCurrentTexture, error);
+ err = UNKNOWN_ERROR;
+ }
+
+ // We destroy the EGLImageKHR here because the current buffer may no
+ // longer be associated with one of the buffer slots, so we have
+ // nowhere to to store it. If the buffer is still associated with a
+ // slot then another EGLImageKHR will be created next time that buffer
+ // gets acquired in updateTexImage.
+ eglDestroyImageKHR(dpy, image);
+
+ return err;
+}
+
+
+status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked");
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- if (useNativeFenceSync) {
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
EGLSyncKHR sync = eglCreateSyncKHR(dpy,
EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
if (sync == EGL_NO_SYNC_KHR) {
@@ -490,7 +516,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
"%s (%d)", strerror(-err), err);
return err;
}
- } else if (mUseFenceSync) {
+ } else if (mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
if (fence != EGL_NO_SYNC_KHR) {
// There is already a fence for the current slot. We need to
@@ -526,7 +552,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
return OK;
}
-bool SurfaceTexture::isExternalFormat(uint32_t format)
+bool GLConsumer::isExternalFormat(uint32_t format)
{
switch (format) {
// supported YUV formats
@@ -545,19 +571,19 @@ bool SurfaceTexture::isExternalFormat(uint32_t format)
return false;
}
-GLenum SurfaceTexture::getCurrentTextureTarget() const {
+GLenum GLConsumer::getCurrentTextureTarget() const {
return mTexTarget;
}
-void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+void GLConsumer::getTransformMatrix(float mtx[16]) {
Mutex::Autolock lock(mMutex);
memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
}
-void SurfaceTexture::setFilteringEnabled(bool enabled) {
+void GLConsumer::setFilteringEnabled(bool enabled) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+ ST_LOGE("setFilteringEnabled: GLConsumer is abandoned!");
return;
}
bool needsRecompute = mFilteringEnabled != enabled;
@@ -572,7 +598,7 @@ void SurfaceTexture::setFilteringEnabled(bool enabled) {
}
}
-void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+void GLConsumer::computeCurrentTransformMatrixLocked() {
ST_LOGV("computeCurrentTransformMatrixLocked");
float xform[16];
@@ -665,19 +691,19 @@ void SurfaceTexture::computeCurrentTransformMatrixLocked() {
mtxMul(mtxBeforeFlipV, crop, xform);
// SurfaceFlinger expects the top of its window textures to be at a Y
- // coordinate of 0, so SurfaceTexture must behave the same way. We don't
+ // coordinate of 0, so GLConsumer must behave the same way. We don't
// want to expose this to applications, however, so we must add an
// additional vertical flip to the transform after all the other transforms.
mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
-nsecs_t SurfaceTexture::getTimestamp() {
+nsecs_t GLConsumer::getTimestamp() {
ST_LOGV("getTimestamp");
Mutex::Autolock lock(mMutex);
return mCurrentTimestamp;
}
-EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
+EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = {
@@ -693,12 +719,12 @@ EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
return image;
}
-sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
+sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
Mutex::Autolock lock(mMutex);
return mCurrentTextureBuf;
}
-Rect SurfaceTexture::getCurrentCrop() const {
+Rect GLConsumer::getCurrentCrop() const {
Mutex::Autolock lock(mMutex);
Rect outCrop = mCurrentCrop;
@@ -734,27 +760,27 @@ Rect SurfaceTexture::getCurrentCrop() const {
return outCrop;
}
-uint32_t SurfaceTexture::getCurrentTransform() const {
+uint32_t GLConsumer::getCurrentTransform() const {
Mutex::Autolock lock(mMutex);
return mCurrentTransform;
}
-uint32_t SurfaceTexture::getCurrentScalingMode() const {
+uint32_t GLConsumer::getCurrentScalingMode() const {
Mutex::Autolock lock(mMutex);
return mCurrentScalingMode;
}
-sp<Fence> SurfaceTexture::getCurrentFence() const {
+sp<Fence> GLConsumer::getCurrentFence() const {
Mutex::Autolock lock(mMutex);
return mCurrentFence;
}
-status_t SurfaceTexture::doGLFenceWait() const {
+status_t GLConsumer::doGLFenceWait() const {
Mutex::Autolock lock(mMutex);
return doGLFenceWaitLocked();
}
-status_t SurfaceTexture::doGLFenceWaitLocked() const {
+status_t GLConsumer::doGLFenceWaitLocked() const {
EGLDisplay dpy = eglGetCurrentDisplay();
EGLContext ctx = eglGetCurrentContext();
@@ -769,8 +795,8 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
return INVALID_OPERATION;
}
- if (mCurrentFence != NULL) {
- if (useWaitSync) {
+ if (mCurrentFence->isValid()) {
+ if (SyncFeatures::getInstance().useWaitSync()) {
// Create an EGLSyncKHR from the current fence.
int fenceFd = mCurrentFence->dup();
if (fenceFd == -1) {
@@ -793,7 +819,7 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
// XXX: The spec draft is inconsistent as to whether this should
// return an EGLint or void. Ignore the return value for now, as
// it's not strictly needed.
- eglWaitSyncANDROID(dpy, sync, 0);
+ eglWaitSyncKHR(dpy, sync, 0);
EGLint eglErr = eglGetError();
eglDestroySyncKHR(dpy, sync);
if (eglErr != EGL_SUCCESS) {
@@ -802,8 +828,8 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
return UNKNOWN_ERROR;
}
} else {
- status_t err = mCurrentFence->waitForever(1000,
- "SurfaceTexture::doGLFenceWaitLocked");
+ status_t err = mCurrentFence->waitForever(
+ "GLConsumer::doGLFenceWaitLocked");
if (err != NO_ERROR) {
ST_LOGE("doGLFenceWait: error waiting for fence: %d", err);
return err;
@@ -814,12 +840,12 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
return NO_ERROR;
}
-bool SurfaceTexture::isSynchronousMode() const {
+bool GLConsumer::isSynchronousMode() const {
Mutex::Autolock lock(mMutex);
return mBufferQueue->isSynchronousMode();
}
-void SurfaceTexture::freeBufferLocked(int slotIndex) {
+void GLConsumer::freeBufferLocked(int slotIndex) {
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
@@ -833,42 +859,42 @@ void SurfaceTexture::freeBufferLocked(int slotIndex) {
ConsumerBase::freeBufferLocked(slotIndex);
}
-void SurfaceTexture::abandonLocked() {
+void GLConsumer::abandonLocked() {
ST_LOGV("abandonLocked");
mCurrentTextureBuf.clear();
ConsumerBase::abandonLocked();
}
-void SurfaceTexture::setName(const String8& name) {
+void GLConsumer::setName(const String8& name) {
Mutex::Autolock _l(mMutex);
mName = name;
mBufferQueue->setConsumerName(name);
}
-status_t SurfaceTexture::setDefaultBufferFormat(uint32_t defaultFormat) {
+status_t GLConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setDefaultBufferFormat(defaultFormat);
}
-status_t SurfaceTexture::setConsumerUsageBits(uint32_t usage) {
+status_t GLConsumer::setConsumerUsageBits(uint32_t usage) {
Mutex::Autolock lock(mMutex);
usage |= DEFAULT_USAGE_FLAGS;
return mBufferQueue->setConsumerUsageBits(usage);
}
-status_t SurfaceTexture::setTransformHint(uint32_t hint) {
+status_t GLConsumer::setTransformHint(uint32_t hint) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setTransformHint(hint);
}
-// Used for refactoring BufferQueue from SurfaceTexture
-// Should not be in final interface once users of SurfaceTexture are clean up.
-status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+// Used for refactoring BufferQueue from GLConsumer
+// Should not be in final interface once users of GLConsumer are clean up.
+status_t GLConsumer::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setSynchronousMode(enabled);
}
-void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
+void GLConsumer::dumpLocked(String8& result, const char* prefix,
char* buffer, size_t size) const
{
snprintf(buffer, size,
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
new file mode 100644
index 0000000..b360e81
--- /dev/null
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -0,0 +1,53 @@
+/*
+ **
+ ** Copyright 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.
+ */
+
+#include <cutils/log.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <gui/GraphicBufferAlloc.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+GraphicBufferAlloc::GraphicBufferAlloc() {
+}
+
+GraphicBufferAlloc::~GraphicBufferAlloc() {
+}
+
+sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t usage, status_t* error) {
+ sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage));
+ status_t err = graphicBuffer->initCheck();
+ *error = err;
+ if (err != 0 || graphicBuffer->handle == 0) {
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
+ ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
+ "failed (%s), handle=%p",
+ w, h, strerror(-err), graphicBuffer->handle);
+ return 0;
+ }
+ return graphicBuffer;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp
index bafd21a..bc0c83c 100644
--- a/libs/gui/GuiConfig.cpp
+++ b/libs/gui/GuiConfig.cpp
@@ -22,14 +22,8 @@ void appendGuiConfigString(String8& configStr)
{
static const char* config =
" [libgui"
-#ifdef USE_FENCE_SYNC
- " USE_FENCE_SYNC"
-#endif
-#ifdef USE_NATIVE_FENCE_SYNC
- " USE_NATIVE_FENCE_SYNC"
-#endif
-#ifdef USE_WAIT_SYNC
- " USE_WAIT_SYNC"
+#ifdef DONT_USE_FENCE_SYNC
+ " DONT_USE_FENCE_SYNC"
#endif
"]";
configStr.append(config);
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/IGraphicBufferProducer.cpp
index a0b1e74..63d7628 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -25,7 +25,7 @@
#include <binder/Parcel.h>
#include <binder/IInterface.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -43,17 +43,17 @@ enum {
};
-class BpSurfaceTexture : public BpInterface<ISurfaceTexture>
+class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
public:
- BpSurfaceTexture(const sp<IBinder>& impl)
- : BpInterface<ISurfaceTexture>(impl)
+ BpGraphicBufferProducer(const sp<IBinder>& impl)
+ : BpInterface<IGraphicBufferProducer>(impl)
{
}
virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
if (result != NO_ERROR) {
@@ -71,7 +71,7 @@ public:
virtual status_t setBufferCount(int bufferCount)
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferCount);
status_t result =remote()->transact(SET_BUFFER_COUNT, data, &reply);
if (result != NO_ERROR) {
@@ -81,10 +81,10 @@ public:
return result;
}
- virtual status_t dequeueBuffer(int *buf, sp<Fence>& fence,
+ virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(w);
data.writeInt32(h);
data.writeInt32(format);
@@ -94,11 +94,13 @@ public:
return result;
}
*buf = reply.readInt32();
- fence.clear();
- bool hasFence = reply.readInt32();
- if (hasFence) {
- fence = new Fence();
- reply.read(*fence.get());
+ bool fenceWasWritten = reply.readInt32();
+ if (fenceWasWritten) {
+ // If the fence was written by the callee, then overwrite the
+ // caller's fence here. If it wasn't written then don't touch the
+ // caller's fence.
+ *fence = new Fence();
+ reply.read(*(fence->get()));
}
result = reply.readInt32();
return result;
@@ -107,7 +109,7 @@ public:
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
data.write(input);
status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
@@ -119,21 +121,17 @@ public:
return result;
}
- virtual void cancelBuffer(int buf, sp<Fence> fence) {
+ virtual void cancelBuffer(int buf, const sp<Fence>& fence) {
Parcel data, reply;
- bool hasFence = fence.get() && fence->isValid();
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
- data.writeInt32(hasFence);
- if (hasFence) {
- data.write(*fence.get());
- }
+ data.write(*fence.get());
remote()->transact(CANCEL_BUFFER, data, &reply);
}
virtual int query(int what, int* value) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(what);
status_t result = remote()->transact(QUERY, data, &reply);
if (result != NO_ERROR) {
@@ -146,7 +144,7 @@ public:
virtual status_t setSynchronousMode(bool enabled) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(enabled);
status_t result = remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply);
if (result != NO_ERROR) {
@@ -158,7 +156,7 @@ public:
virtual status_t connect(int api, QueueBufferOutput* output) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(api);
status_t result = remote()->transact(CONNECT, data, &reply);
if (result != NO_ERROR) {
@@ -171,7 +169,7 @@ public:
virtual status_t disconnect(int api) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(api);
status_t result =remote()->transact(DISCONNECT, data, &reply);
if (result != NO_ERROR) {
@@ -182,16 +180,16 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
+IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
// ----------------------------------------------------------------------
-status_t BnSurfaceTexture::onTransact(
+status_t BnGraphicBufferProducer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case REQUEST_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
@@ -203,32 +201,31 @@ status_t BnSurfaceTexture::onTransact(
return NO_ERROR;
} break;
case SET_BUFFER_COUNT: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferCount = data.readInt32();
int result = setBufferCount(bufferCount);
reply->writeInt32(result);
return NO_ERROR;
} break;
case DEQUEUE_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
uint32_t w = data.readInt32();
uint32_t h = data.readInt32();
uint32_t format = data.readInt32();
uint32_t usage = data.readInt32();
int buf;
sp<Fence> fence;
- int result = dequeueBuffer(&buf, fence, w, h, format, usage);
- bool hasFence = fence.get() && fence->isValid();
+ int result = dequeueBuffer(&buf, &fence, w, h, format, usage);
reply->writeInt32(buf);
- reply->writeInt32(hasFence);
- if (hasFence) {
+ reply->writeInt32(fence != NULL);
+ if (fence != NULL) {
reply->write(*fence.get());
}
reply->writeInt32(result);
return NO_ERROR;
} break;
case QUEUE_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
QueueBufferInput input(data);
QueueBufferOutput* const output =
@@ -239,19 +236,15 @@ status_t BnSurfaceTexture::onTransact(
return NO_ERROR;
} break;
case CANCEL_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
- sp<Fence> fence;
- bool hasFence = data.readInt32();
- if (hasFence) {
- fence = new Fence();
- data.read(*fence.get());
- }
+ sp<Fence> fence = new Fence();
+ data.read(*fence.get());
cancelBuffer(buf, fence);
return NO_ERROR;
} break;
case QUERY: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int value;
int what = data.readInt32();
int res = query(what, &value);
@@ -260,14 +253,14 @@ status_t BnSurfaceTexture::onTransact(
return NO_ERROR;
} break;
case SET_SYNCHRONOUS_MODE: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
bool enabled = data.readInt32();
status_t res = setSynchronousMode(enabled);
reply->writeInt32(res);
return NO_ERROR;
} break;
case CONNECT: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int api = data.readInt32();
QueueBufferOutput* const output =
reinterpret_cast<QueueBufferOutput *>(
@@ -277,7 +270,7 @@ status_t BnSurfaceTexture::onTransact(
return NO_ERROR;
} break;
case DISCONNECT: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int api = data.readInt32();
status_t res = disconnect(api);
reply->writeInt32(res);
@@ -289,61 +282,48 @@ status_t BnSurfaceTexture::onTransact(
// ----------------------------------------------------------------------------
-static bool isValid(const sp<Fence>& fence) {
- return fence.get() && fence->isValid();
-}
-
-ISurfaceTexture::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
+IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
parcel.read(*this);
}
-size_t ISurfaceTexture::QueueBufferInput::getFlattenedSize() const
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const
{
return sizeof(timestamp)
+ sizeof(crop)
+ sizeof(scalingMode)
+ sizeof(transform)
- + sizeof(bool)
- + (isValid(fence) ? fence->getFlattenedSize() : 0);
+ + fence->getFlattenedSize();
}
-size_t ISurfaceTexture::QueueBufferInput::getFdCount() const
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const
{
- return isValid(fence) ? fence->getFdCount() : 0;
+ return fence->getFdCount();
}
-status_t ISurfaceTexture::QueueBufferInput::flatten(void* buffer, size_t size,
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(void* buffer, size_t size,
int fds[], size_t count) const
{
status_t err = NO_ERROR;
- bool haveFence = isValid(fence);
char* p = (char*)buffer;
memcpy(p, &timestamp, sizeof(timestamp)); p += sizeof(timestamp);
memcpy(p, &crop, sizeof(crop)); p += sizeof(crop);
memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode);
memcpy(p, &transform, sizeof(transform)); p += sizeof(transform);
- memcpy(p, &haveFence, sizeof(haveFence)); p += sizeof(haveFence);
- if (haveFence) {
- err = fence->flatten(p, size - (p - (char*)buffer), fds, count);
- }
+ err = fence->flatten(p, size - (p - (char*)buffer), fds, count);
return err;
}
-status_t ISurfaceTexture::QueueBufferInput::unflatten(void const* buffer,
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(void const* buffer,
size_t size, int fds[], size_t count)
{
status_t err = NO_ERROR;
- bool haveFence;
const char* p = (const char*)buffer;
memcpy(&timestamp, p, sizeof(timestamp)); p += sizeof(timestamp);
memcpy(&crop, p, sizeof(crop)); p += sizeof(crop);
memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode);
memcpy(&transform, p, sizeof(transform)); p += sizeof(transform);
- memcpy(&haveFence, p, sizeof(haveFence)); p += sizeof(haveFence);
- if (haveFence) {
- fence = new Fence();
- err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count);
- }
+ fence = new Fence();
+ err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count);
return err;
}
diff --git a/libs/gui/ISurface.cpp b/libs/gui/ISurface.cpp
deleted file mode 100644
index c2ea183..0000000
--- a/libs/gui/ISurface.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2007 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 LOG_TAG "ISurface"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <gui/ISurface.h>
-#include <gui/ISurfaceTexture.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpSurface : public BpInterface<ISurface>
-{
-public:
- BpSurface(const sp<IBinder>& impl)
- : BpInterface<ISurface>(impl)
- {
- }
-
- virtual sp<ISurfaceTexture> getSurfaceTexture() const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
- remote()->transact(GET_SURFACE_TEXTURE, data, &reply);
- return interface_cast<ISurfaceTexture>(reply.readStrongBinder());
- }
-};
-
-IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface");
-
-// ----------------------------------------------------------------------
-
-status_t BnSurface::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GET_SURFACE_TEXTURE: {
- CHECK_INTERFACE(ISurface, data, reply);
- reply->writeStrongBinder( getSurfaceTexture()->asBinder() );
- return NO_ERROR;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 85a9488..6442a86 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -28,7 +28,7 @@
#include <gui/BitTube.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <private/gui/LayerState.h>
@@ -102,29 +102,27 @@ public:
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
- virtual status_t captureScreen(
- const sp<IBinder>& display, sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format,
+ virtual status_t captureScreen(const sp<IBinder>& display,
+ const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ)
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool isCpuConsumer)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
+ data.writeStrongBinder(producer->asBinder());
data.writeInt32(reqWidth);
data.writeInt32(reqHeight);
data.writeInt32(minLayerZ);
data.writeInt32(maxLayerZ);
+ data.writeInt32(isCpuConsumer);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
- *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
- *width = reply.readInt32();
- *height = reply.readInt32();
- *format = reply.readInt32();
return reply.readInt32();
}
virtual bool authenticateSurfaceTexture(
- const sp<ISurfaceTexture>& surfaceTexture) const
+ const sp<IGraphicBufferProducer>& bufferProducer) const
{
Parcel data, reply;
int err = NO_ERROR;
@@ -135,7 +133,7 @@ public:
"interface descriptor: %s (%d)", strerror(-err), -err);
return false;
}
- err = data.writeStrongBinder(surfaceTexture->asBinder());
+ err = data.writeStrongBinder(bufferProducer->asBinder());
if (err != NO_ERROR) {
ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing "
"strong binder to parcel: %s (%d)", strerror(-err), -err);
@@ -271,26 +269,23 @@ status_t BnSurfaceComposer::onTransact(
case CAPTURE_SCREEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = data.readStrongBinder();
+ sp<IGraphicBufferProducer> producer =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
uint32_t reqWidth = data.readInt32();
uint32_t reqHeight = data.readInt32();
uint32_t minLayerZ = data.readInt32();
uint32_t maxLayerZ = data.readInt32();
- sp<IMemoryHeap> heap;
- uint32_t w, h;
- PixelFormat f;
- status_t res = captureScreen(display, &heap, &w, &h, &f,
- reqWidth, reqHeight, minLayerZ, maxLayerZ);
- reply->writeStrongBinder(heap->asBinder());
- reply->writeInt32(w);
- reply->writeInt32(h);
- reply->writeInt32(f);
+ bool isCpuConsumer = data.readInt32();
+ status_t res = captureScreen(display, producer,
+ reqWidth, reqHeight, minLayerZ, maxLayerZ,
+ isCpuConsumer);
reply->writeInt32(res);
} break;
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<ISurfaceTexture> surfaceTexture =
- interface_cast<ISurfaceTexture>(data.readStrongBinder());
- int32_t result = authenticateSurfaceTexture(surfaceTexture) ? 1 : 0;
+ sp<IGraphicBufferProducer> bufferProducer =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+ int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0;
reply->writeInt32(result);
} break;
case CREATE_DISPLAY_EVENT_CONNECTION: {
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 8f7bc05..1adc134 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -29,7 +29,7 @@
#include <ui/Point.h>
#include <ui/Rect.h>
-#include <gui/ISurface.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposerClient.h>
#include <private/gui/LayerState.h>
@@ -46,17 +46,13 @@ class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
{
public:
BpSurfaceComposerClient(const sp<IBinder>& impl)
- : BpInterface<ISurfaceComposerClient>(impl)
- {
+ : BpInterface<ISurfaceComposerClient>(impl) {
}
- virtual sp<ISurface> createSurface( surface_data_t* params,
- const String8& name,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- uint32_t flags)
- {
+ virtual status_t createSurface(const String8& name, uint32_t w,
+ uint32_t h, PixelFormat format, uint32_t flags,
+ sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
data.writeString8(name);
@@ -65,15 +61,15 @@ public:
data.writeInt32(format);
data.writeInt32(flags);
remote()->transact(CREATE_SURFACE, data, &reply);
- params->readFromParcel(reply);
- return interface_cast<ISurface>(reply.readStrongBinder());
+ *handle = reply.readStrongBinder();
+ *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
+ return reply.readInt32();
}
- virtual status_t destroySurface(SurfaceID sid)
- {
+ virtual status_t destroySurface(const sp<IBinder>& handle) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeInt32(sid);
+ data.writeStrongBinder(handle);
remote()->transact(DESTROY_SURFACE, data, &reply);
return reply.readInt32();
}
@@ -89,21 +85,23 @@ status_t BnSurfaceComposerClient::onTransact(
switch(code) {
case CREATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- surface_data_t params;
String8 name = data.readString8();
uint32_t w = data.readInt32();
uint32_t h = data.readInt32();
PixelFormat format = data.readInt32();
uint32_t flags = data.readInt32();
- sp<ISurface> s = createSurface(&params, name, w, h,
- format, flags);
- params.writeToParcel(reply);
- reply->writeStrongBinder(s->asBinder());
+ sp<IBinder> handle;
+ sp<IGraphicBufferProducer> gbp;
+ status_t result = createSurface(name, w, h, format, flags,
+ &handle, &gbp);
+ reply->writeStrongBinder(handle);
+ reply->writeStrongBinder(gbp->asBinder());
+ reply->writeInt32(result);
return NO_ERROR;
} break;
case DESTROY_SURFACE: {
CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- reply->writeInt32( destroySurface( data.readInt32() ) );
+ reply->writeInt32( destroySurface( data.readStrongBinder() ) );
return NO_ERROR;
} break;
default:
@@ -111,20 +109,4 @@ status_t BnSurfaceComposerClient::onTransact(
}
}
-// ----------------------------------------------------------------------
-
-status_t ISurfaceComposerClient::surface_data_t::readFromParcel(const Parcel& parcel)
-{
- token = parcel.readInt32();
- identity = parcel.readInt32();
- return NO_ERROR;
-}
-
-status_t ISurfaceComposerClient::surface_data_t::writeToParcel(Parcel* parcel) const
-{
- parcel->writeInt32(token);
- parcel->writeInt32(identity);
- return NO_ERROR;
-}
-
}; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e2604f8..acdbd77 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -17,36 +17,48 @@
#include <utils/Errors.h>
#include <binder/Parcel.h>
#include <gui/ISurfaceComposerClient.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <private/gui/LayerState.h>
namespace android {
status_t layer_state_t::write(Parcel& output) const
{
- status_t err;
-
- err = output.write(transparentRegion);
- if (err < NO_ERROR) return err;
-
- // NOTE: regions are at the end of the structure
- size_t size = sizeof(layer_state_t);
- size -= sizeof(transparentRegion);
- err = output.write(this, size);
- return err;
+ output.writeStrongBinder(surface);
+ output.writeInt32(what);
+ output.writeFloat(x);
+ output.writeFloat(y);
+ output.writeInt32(z);
+ output.writeInt32(w);
+ output.writeInt32(h);
+ output.writeInt32(layerStack);
+ output.writeFloat(alpha);
+ output.writeInt32(flags);
+ output.writeInt32(mask);
+ *reinterpret_cast<layer_state_t::matrix22_t *>(
+ output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
+ output.write(crop);
+ output.write(transparentRegion);
+ return NO_ERROR;
}
status_t layer_state_t::read(const Parcel& input)
{
- status_t err;
-
- err = input.read(transparentRegion);
- if (err < NO_ERROR) return err;
-
- // NOTE: regions are at the end of the structure
- size_t size = sizeof(layer_state_t);
- size -= sizeof(transparentRegion);
- input.read(this, size);
+ surface = input.readStrongBinder();
+ what = input.readInt32();
+ x = input.readFloat();
+ y = input.readFloat();
+ z = input.readInt32();
+ w = input.readInt32();
+ h = input.readInt32();
+ layerStack = input.readInt32();
+ alpha = input.readFloat();
+ flags = input.readInt32();
+ mask = input.readInt32();
+ matrix = *reinterpret_cast<layer_state_t::matrix22_t const *>(
+ input.readInplace(sizeof(layer_state_t::matrix22_t)));
+ input.read(crop);
+ input.read(transparentRegion);
return NO_ERROR;
}
@@ -74,7 +86,7 @@ status_t DisplayState::write(Parcel& output) const {
status_t DisplayState::read(const Parcel& input) {
token = input.readStrongBinder();
- surface = interface_cast<ISurfaceTexture>(input.readStrongBinder());
+ surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
what = input.readInt32();
layerStack = input.readInt32();
orientation = input.readInt32();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 1745061..a616c1e 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -15,371 +15,820 @@
*/
#define LOG_TAG "Surface"
-
-#include <stdint.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
#include <android/native_window.h>
-#include <utils/CallStack.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
+#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
-#include <ui/DisplayInfo.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
+#include <ui/Fence.h>
-#include <gui/ISurface.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/GLConsumer.h>
+#include <gui/Surface.h>
-namespace android {
+#include <private/gui/ComposerService.h>
-// ============================================================================
-// SurfaceControl
-// ============================================================================
+namespace android {
-SurfaceControl::SurfaceControl(
- const sp<SurfaceComposerClient>& client,
- const sp<ISurface>& surface,
- const ISurfaceComposerClient::surface_data_t& data)
- : mClient(client), mSurface(surface),
- mToken(data.token), mIdentity(data.identity)
+Surface::Surface(
+ const sp<IGraphicBufferProducer>& bufferProducer)
+ : mGraphicBufferProducer(bufferProducer)
{
+ // Initialize the ANativeWindow function pointers.
+ ANativeWindow::setSwapInterval = hook_setSwapInterval;
+ ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
+ ANativeWindow::cancelBuffer = hook_cancelBuffer;
+ ANativeWindow::queueBuffer = hook_queueBuffer;
+ ANativeWindow::query = hook_query;
+ ANativeWindow::perform = hook_perform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
+ ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
+ ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
+ ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
+
+ const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
+ const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+
+ mReqWidth = 0;
+ mReqHeight = 0;
+ mReqFormat = 0;
+ mReqUsage = 0;
+ mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ mCrop.clear();
+ mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ mTransform = 0;
+ mDefaultWidth = 0;
+ mDefaultHeight = 0;
+ mUserWidth = 0;
+ mUserHeight = 0;
+ mTransformHint = 0;
+ mConsumerRunningBehind = false;
+ mConnectedToCpu = false;
+}
+
+Surface::~Surface() {
+ if (mConnectedToCpu) {
+ Surface::disconnect(NATIVE_WINDOW_API_CPU);
+ }
}
-
-SurfaceControl::~SurfaceControl()
-{
- destroy();
+
+sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
+ return mGraphicBufferProducer;
}
-void SurfaceControl::destroy()
-{
- if (isValid()) {
- mClient->destroySurface(mToken);
- }
+int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
+ Surface* c = getSelf(window);
+ return c->setSwapInterval(interval);
+}
- // clear all references and trigger an IPC now, to make sure things
- // happen without delay, since these resources are quite heavy.
- mClient.clear();
- mSurface.clear();
- IPCThreadState::self()->flushCommands();
+int Surface::hook_dequeueBuffer(ANativeWindow* window,
+ ANativeWindowBuffer** buffer, int* fenceFd) {
+ Surface* c = getSelf(window);
+ return c->dequeueBuffer(buffer, fenceFd);
}
-void SurfaceControl::clear()
-{
- // here, the window manager tells us explicitly that we should destroy
- // the surface's resource. Soon after this call, it will also release
- // its last reference (which will call the dtor); however, it is possible
- // that a client living in the same process still holds references which
- // would delay the call to the dtor -- that is why we need this explicit
- // "clear()" call.
- destroy();
+int Surface::hook_cancelBuffer(ANativeWindow* window,
+ ANativeWindowBuffer* buffer, int fenceFd) {
+ Surface* c = getSelf(window);
+ return c->cancelBuffer(buffer, fenceFd);
}
-bool SurfaceControl::isSameSurface(
- const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs)
-{
- if (lhs == 0 || rhs == 0)
- return false;
- return lhs->mSurface->asBinder() == rhs->mSurface->asBinder();
-}
-
-status_t SurfaceControl::setLayerStack(int32_t layerStack) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setLayerStack(mToken, layerStack);
-}
-status_t SurfaceControl::setLayer(int32_t layer) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setLayer(mToken, layer);
-}
-status_t SurfaceControl::setPosition(int32_t x, int32_t y) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setPosition(mToken, x, y);
-}
-status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setSize(mToken, w, h);
-}
-status_t SurfaceControl::hide() {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->hide(mToken);
-}
-status_t SurfaceControl::show() {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->show(mToken);
-}
-status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setFlags(mToken, flags, mask);
-}
-status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setTransparentRegionHint(mToken, transparent);
-}
-status_t SurfaceControl::setAlpha(float alpha) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setAlpha(mToken, alpha);
-}
-status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy);
-}
-status_t SurfaceControl::setCrop(const Rect& crop) {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->setCrop(mToken, crop);
-}
-
-status_t SurfaceControl::validate() const
-{
- if (mToken<0 || mClient==0) {
- ALOGE("invalid token (%d, identity=%u) or client (%p)",
- mToken, mIdentity, mClient.get());
- return NO_INIT;
+int Surface::hook_queueBuffer(ANativeWindow* window,
+ ANativeWindowBuffer* buffer, int fenceFd) {
+ Surface* c = getSelf(window);
+ return c->queueBuffer(buffer, fenceFd);
+}
+
+int Surface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer) {
+ Surface* c = getSelf(window);
+ ANativeWindowBuffer* buf;
+ int fenceFd = -1;
+ int result = c->dequeueBuffer(&buf, &fenceFd);
+ sp<Fence> fence(new Fence(fenceFd));
+ int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
+ if (waitResult != OK) {
+ ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
+ waitResult);
+ c->cancelBuffer(buf, -1);
+ return waitResult;
}
- return NO_ERROR;
+ *buffer = buf;
+ return result;
}
-status_t SurfaceControl::writeSurfaceToParcel(
- const sp<SurfaceControl>& control, Parcel* parcel)
-{
- sp<ISurface> sur;
- uint32_t identity = 0;
- if (SurfaceControl::isValid(control)) {
- sur = control->mSurface;
- identity = control->mIdentity;
+int Surface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ Surface* c = getSelf(window);
+ return c->cancelBuffer(buffer, -1);
+}
+
+int Surface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ Surface* c = getSelf(window);
+ return c->lockBuffer_DEPRECATED(buffer);
+}
+
+int Surface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ Surface* c = getSelf(window);
+ return c->queueBuffer(buffer, -1);
+}
+
+int Surface::hook_query(const ANativeWindow* window,
+ int what, int* value) {
+ const Surface* c = getSelf(window);
+ return c->query(what, value);
+}
+
+int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
+ va_list args;
+ va_start(args, operation);
+ Surface* c = getSelf(window);
+ return c->perform(operation, args);
+}
+
+int Surface::setSwapInterval(int interval) {
+ ATRACE_CALL();
+ // EGL specification states:
+ // interval is silently clamped to minimum and maximum implementation
+ // dependent values before being stored.
+ // Although we don't have to, we apply the same logic here.
+
+ if (interval < minSwapInterval)
+ interval = minSwapInterval;
+
+ if (interval > maxSwapInterval)
+ interval = maxSwapInterval;
+
+ status_t res = mGraphicBufferProducer->setSynchronousMode(interval ? true : false);
+
+ return res;
+}
+
+int Surface::dequeueBuffer(android_native_buffer_t** buffer,
+ int* fenceFd) {
+ ATRACE_CALL();
+ ALOGV("Surface::dequeueBuffer");
+ Mutex::Autolock lock(mMutex);
+ int buf = -1;
+ int reqW = mReqWidth ? mReqWidth : mUserWidth;
+ int reqH = mReqHeight ? mReqHeight : mUserHeight;
+ sp<Fence> fence;
+ status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
+ reqW, reqH, mReqFormat, mReqUsage);
+ if (result < 0) {
+ ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)"
+ "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
+ result);
+ return result;
}
- parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
- parcel->writeStrongBinder(NULL); // NULL ISurfaceTexture in this case.
- parcel->writeInt32(identity);
- return NO_ERROR;
+ sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
+ if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+ freeAllBuffers();
+ }
+
+ if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+ result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
+ if (result != NO_ERROR) {
+ ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d",
+ result);
+ return result;
+ }
+ }
+
+ if (fence->isValid()) {
+ *fenceFd = fence->dup();
+ if (*fenceFd == -1) {
+ ALOGE("dequeueBuffer: error duping fence: %d", errno);
+ // dup() should never fail; something is badly wrong. Soldier on
+ // and hope for the best; the worst that should happen is some
+ // visible corruption that lasts until the next frame.
+ }
+ } else {
+ *fenceFd = -1;
+ }
+
+ *buffer = gbuf.get();
+ return OK;
}
-sp<Surface> SurfaceControl::getSurface() const
-{
- Mutex::Autolock _l(mLock);
- if (mSurfaceData == 0) {
- sp<SurfaceControl> surface_control(const_cast<SurfaceControl*>(this));
- mSurfaceData = new Surface(surface_control);
+int Surface::cancelBuffer(android_native_buffer_t* buffer,
+ int fenceFd) {
+ ATRACE_CALL();
+ ALOGV("Surface::cancelBuffer");
+ Mutex::Autolock lock(mMutex);
+ int i = getSlotFromBufferLocked(buffer);
+ if (i < 0) {
+ return i;
+ }
+ sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ mGraphicBufferProducer->cancelBuffer(i, fence);
+ return OK;
+}
+
+int Surface::getSlotFromBufferLocked(
+ android_native_buffer_t* buffer) const {
+ bool dumpedState = false;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].buffer != NULL &&
+ mSlots[i].buffer->handle == buffer->handle) {
+ return i;
+ }
}
- return mSurfaceData;
+ ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+ return BAD_VALUE;
}
-// ============================================================================
-// Surface
-// ============================================================================
+int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) {
+ ALOGV("Surface::lockBuffer");
+ Mutex::Autolock lock(mMutex);
+ return OK;
+}
-// ---------------------------------------------------------------------------
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffer");
+ Mutex::Autolock lock(mMutex);
+ int64_t timestamp;
+ if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+ timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
+ timestamp / 1000000.f);
+ } else {
+ timestamp = mTimestamp;
+ }
+ int i = getSlotFromBufferLocked(buffer);
+ if (i < 0) {
+ return i;
+ }
-Surface::Surface(const sp<SurfaceControl>& surface)
- : SurfaceTextureClient(),
- mSurface(surface->mSurface),
- mIdentity(surface->mIdentity)
-{
- sp<ISurfaceTexture> st;
- if (mSurface != NULL) {
- st = mSurface->getSurfaceTexture();
+
+ // Make sure the crop rectangle is entirely inside the buffer.
+ Rect crop;
+ mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
+
+ sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode,
+ mTransform, fence);
+ status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+ if (err != OK) {
+ ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
+ }
+ uint32_t numPendingBuffers = 0;
+ output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
+ &numPendingBuffers);
+
+ mConsumerRunningBehind = (numPendingBuffers >= 2);
+
+ return err;
+}
+
+int Surface::query(int what, int* value) const {
+ ATRACE_CALL();
+ ALOGV("Surface::query");
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ switch (what) {
+ case NATIVE_WINDOW_FORMAT:
+ if (mReqFormat) {
+ *value = mReqFormat;
+ return NO_ERROR;
+ }
+ break;
+ case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
+ sp<ISurfaceComposer> composer(
+ ComposerService::getComposerService());
+ if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
+ *value = 1;
+ } else {
+ *value = 0;
+ }
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ *value = NATIVE_WINDOW_SURFACE;
+ return NO_ERROR;
+ case NATIVE_WINDOW_DEFAULT_WIDTH:
+ *value = mUserWidth ? mUserWidth : mDefaultWidth;
+ return NO_ERROR;
+ case NATIVE_WINDOW_DEFAULT_HEIGHT:
+ *value = mUserHeight ? mUserHeight : mDefaultHeight;
+ return NO_ERROR;
+ case NATIVE_WINDOW_TRANSFORM_HINT:
+ *value = mTransformHint;
+ return NO_ERROR;
+ case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
+ status_t err = NO_ERROR;
+ if (!mConsumerRunningBehind) {
+ *value = 0;
+ } else {
+ err = mGraphicBufferProducer->query(what, value);
+ if (err == NO_ERROR) {
+ mConsumerRunningBehind = *value;
+ }
+ }
+ return err;
+ }
+ }
}
- init(st);
+ return mGraphicBufferProducer->query(what, value);
}
-Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
- : SurfaceTextureClient()
+int Surface::perform(int operation, va_list args)
{
- mSurface = interface_cast<ISurface>(ref);
- sp<IBinder> st_binder(parcel.readStrongBinder());
- sp<ISurfaceTexture> st;
- if (st_binder != NULL) {
- st = interface_cast<ISurfaceTexture>(st_binder);
- } else if (mSurface != NULL) {
- st = mSurface->getSurfaceTexture();
+ int res = NO_ERROR;
+ switch (operation) {
+ case NATIVE_WINDOW_CONNECT:
+ // deprecated. must return NO_ERROR.
+ break;
+ case NATIVE_WINDOW_DISCONNECT:
+ // deprecated. must return NO_ERROR.
+ break;
+ case NATIVE_WINDOW_SET_USAGE:
+ res = dispatchSetUsage(args);
+ break;
+ case NATIVE_WINDOW_SET_CROP:
+ res = dispatchSetCrop(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFER_COUNT:
+ res = dispatchSetBufferCount(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ res = dispatchSetBuffersGeometry(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+ res = dispatchSetBuffersTransform(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ res = dispatchSetBuffersTimestamp(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
+ res = dispatchSetBuffersDimensions(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
+ res = dispatchSetBuffersUserDimensions(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ res = dispatchSetBuffersFormat(args);
+ break;
+ case NATIVE_WINDOW_LOCK:
+ res = dispatchLock(args);
+ break;
+ case NATIVE_WINDOW_UNLOCK_AND_POST:
+ res = dispatchUnlockAndPost(args);
+ break;
+ case NATIVE_WINDOW_SET_SCALING_MODE:
+ res = dispatchSetScalingMode(args);
+ break;
+ case NATIVE_WINDOW_API_CONNECT:
+ res = dispatchConnect(args);
+ break;
+ case NATIVE_WINDOW_API_DISCONNECT:
+ res = dispatchDisconnect(args);
+ break;
+ default:
+ res = NAME_NOT_FOUND;
+ break;
}
+ return res;
+}
- mIdentity = parcel.readInt32();
- init(st);
+int Surface::dispatchConnect(va_list args) {
+ int api = va_arg(args, int);
+ return connect(api);
}
-Surface::Surface(const sp<ISurfaceTexture>& st)
- : SurfaceTextureClient(),
- mSurface(NULL),
- mIdentity(0)
-{
- init(st);
+int Surface::dispatchDisconnect(va_list args) {
+ int api = va_arg(args, int);
+ return disconnect(api);
}
-status_t Surface::writeToParcel(
- const sp<Surface>& surface, Parcel* parcel)
-{
- sp<ISurface> sur;
- sp<ISurfaceTexture> st;
- uint32_t identity = 0;
- if (Surface::isValid(surface)) {
- sur = surface->mSurface;
- st = surface->getISurfaceTexture();
- identity = surface->mIdentity;
- } else if (surface != 0 &&
- (surface->mSurface != NULL ||
- surface->getISurfaceTexture() != NULL)) {
- ALOGE("Parceling invalid surface with non-NULL ISurface/ISurfaceTexture as NULL: "
- "mSurface = %p, surfaceTexture = %p, mIdentity = %d, ",
- surface->mSurface.get(), surface->getISurfaceTexture().get(),
- surface->mIdentity);
+int Surface::dispatchSetUsage(va_list args) {
+ int usage = va_arg(args, int);
+ return setUsage(usage);
+}
+
+int Surface::dispatchSetCrop(va_list args) {
+ android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
+ return setCrop(reinterpret_cast<Rect const*>(rect));
+}
+
+int Surface::dispatchSetBufferCount(va_list args) {
+ size_t bufferCount = va_arg(args, size_t);
+ return setBufferCount(bufferCount);
+}
+
+int Surface::dispatchSetBuffersGeometry(va_list args) {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ int f = va_arg(args, int);
+ int err = setBuffersDimensions(w, h);
+ if (err != 0) {
+ return err;
}
+ return setBuffersFormat(f);
+}
- parcel->writeStrongBinder(sur != NULL ? sur->asBinder() : NULL);
- parcel->writeStrongBinder(st != NULL ? st->asBinder() : NULL);
- parcel->writeInt32(identity);
- return NO_ERROR;
+int Surface::dispatchSetBuffersDimensions(va_list args) {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ return setBuffersDimensions(w, h);
+}
+int Surface::dispatchSetBuffersUserDimensions(va_list args) {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ return setBuffersUserDimensions(w, h);
}
-Mutex Surface::sCachedSurfacesLock;
-DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces;
+int Surface::dispatchSetBuffersFormat(va_list args) {
+ int f = va_arg(args, int);
+ return setBuffersFormat(f);
+}
-sp<Surface> Surface::readFromParcel(const Parcel& data) {
- Mutex::Autolock _l(sCachedSurfacesLock);
- sp<IBinder> binder(data.readStrongBinder());
- sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
- if (surface == 0) {
- surface = new Surface(data, binder);
- sCachedSurfaces.add(binder, surface);
- } else {
- // The Surface was found in the cache, but we still should clear any
- // remaining data from the parcel.
- data.readStrongBinder(); // ISurfaceTexture
- data.readInt32(); // identity
+int Surface::dispatchSetScalingMode(va_list args) {
+ int m = va_arg(args, int);
+ return setScalingMode(m);
+}
+
+int Surface::dispatchSetBuffersTransform(va_list args) {
+ int transform = va_arg(args, int);
+ return setBuffersTransform(transform);
+}
+
+int Surface::dispatchSetBuffersTimestamp(va_list args) {
+ int64_t timestamp = va_arg(args, int64_t);
+ return setBuffersTimestamp(timestamp);
+}
+
+int Surface::dispatchLock(va_list args) {
+ ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
+ ARect* inOutDirtyBounds = va_arg(args, ARect*);
+ return lock(outBuffer, inOutDirtyBounds);
+}
+
+int Surface::dispatchUnlockAndPost(va_list args) {
+ return unlockAndPost();
+}
+
+
+int Surface::connect(int api) {
+ ATRACE_CALL();
+ ALOGV("Surface::connect");
+ Mutex::Autolock lock(mMutex);
+ IGraphicBufferProducer::QueueBufferOutput output;
+ int err = mGraphicBufferProducer->connect(api, &output);
+ if (err == NO_ERROR) {
+ uint32_t numPendingBuffers = 0;
+ output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
+ &numPendingBuffers);
+ mConsumerRunningBehind = (numPendingBuffers >= 2);
}
- if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
- surface = 0;
+ if (!err && api == NATIVE_WINDOW_API_CPU) {
+ mConnectedToCpu = true;
}
- cleanCachedSurfacesLocked();
- return surface;
+ return err;
}
-// Remove the stale entries from the surface cache. This should only be called
-// with sCachedSurfacesLock held.
-void Surface::cleanCachedSurfacesLocked() {
- for (int i = sCachedSurfaces.size()-1; i >= 0; --i) {
- wp<Surface> s(sCachedSurfaces.valueAt(i));
- if (s == 0 || s.promote() == 0) {
- sCachedSurfaces.removeItemsAt(i);
+int Surface::disconnect(int api) {
+ ATRACE_CALL();
+ ALOGV("Surface::disconnect");
+ Mutex::Autolock lock(mMutex);
+ freeAllBuffers();
+ int err = mGraphicBufferProducer->disconnect(api);
+ if (!err) {
+ mReqFormat = 0;
+ mReqWidth = 0;
+ mReqHeight = 0;
+ mReqUsage = 0;
+ mCrop.clear();
+ mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ mTransform = 0;
+ if (api == NATIVE_WINDOW_API_CPU) {
+ mConnectedToCpu = false;
}
}
+ return err;
}
-void Surface::init(const sp<ISurfaceTexture>& surfaceTexture)
+int Surface::setUsage(uint32_t reqUsage)
{
- if (mSurface != NULL || surfaceTexture != NULL) {
- ALOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface");
- if (surfaceTexture != NULL) {
- setISurfaceTexture(surfaceTexture);
- setUsage(GraphicBuffer::USAGE_HW_RENDER);
- }
+ ALOGV("Surface::setUsage");
+ Mutex::Autolock lock(mMutex);
+ mReqUsage = reqUsage;
+ return OK;
+}
+
+int Surface::setCrop(Rect const* rect)
+{
+ ATRACE_CALL();
- // TODO: the display metrics should come from the display manager
- DisplayInfo dinfo;
- sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain);
- SurfaceComposerClient::getDisplayInfo(display, &dinfo);
- const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi;
- const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi;
- const_cast<uint32_t&>(ANativeWindow::flags) = 0;
+ Rect realRect;
+ if (rect == NULL || rect->isEmpty()) {
+ realRect.clear();
+ } else {
+ realRect = *rect;
}
+
+ ALOGV("Surface::setCrop rect=[%d %d %d %d]",
+ realRect.left, realRect.top, realRect.right, realRect.bottom);
+
+ Mutex::Autolock lock(mMutex);
+ mCrop = realRect;
+ return NO_ERROR;
}
-Surface::~Surface()
+int Surface::setBufferCount(int bufferCount)
{
- // clear all references and trigger an IPC now, to make sure things
- // happen without delay, since these resources are quite heavy.
- mSurface.clear();
- IPCThreadState::self()->flushCommands();
-}
+ ATRACE_CALL();
+ ALOGV("Surface::setBufferCount");
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = mGraphicBufferProducer->setBufferCount(bufferCount);
+ ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
+ bufferCount, strerror(-err));
-bool Surface::isValid() {
- return getISurfaceTexture() != NULL;
+ if (err == NO_ERROR) {
+ freeAllBuffers();
+ }
+
+ return err;
}
-sp<ISurfaceTexture> Surface::getSurfaceTexture() {
- return getISurfaceTexture();
+int Surface::setBuffersDimensions(int w, int h)
+{
+ ATRACE_CALL();
+ ALOGV("Surface::setBuffersDimensions");
+
+ if (w<0 || h<0)
+ return BAD_VALUE;
+
+ if ((w && !h) || (!w && h))
+ return BAD_VALUE;
+
+ Mutex::Autolock lock(mMutex);
+ mReqWidth = w;
+ mReqHeight = h;
+ return NO_ERROR;
}
-sp<IBinder> Surface::asBinder() const {
- return mSurface!=0 ? mSurface->asBinder() : 0;
+int Surface::setBuffersUserDimensions(int w, int h)
+{
+ ATRACE_CALL();
+ ALOGV("Surface::setBuffersUserDimensions");
+
+ if (w<0 || h<0)
+ return BAD_VALUE;
+
+ if ((w && !h) || (!w && h))
+ return BAD_VALUE;
+
+ Mutex::Autolock lock(mMutex);
+ mUserWidth = w;
+ mUserHeight = h;
+ return NO_ERROR;
}
-// ----------------------------------------------------------------------------
+int Surface::setBuffersFormat(int format)
+{
+ ALOGV("Surface::setBuffersFormat");
-int Surface::query(int what, int* value) const {
- switch (what) {
- case NATIVE_WINDOW_CONCRETE_TYPE:
- *value = NATIVE_WINDOW_SURFACE;
- return NO_ERROR;
+ if (format<0)
+ return BAD_VALUE;
+
+ Mutex::Autolock lock(mMutex);
+ mReqFormat = format;
+ return NO_ERROR;
+}
+
+int Surface::setScalingMode(int mode)
+{
+ ATRACE_CALL();
+ ALOGV("Surface::setScalingMode(%d)", mode);
+
+ switch (mode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+ break;
+ default:
+ ALOGE("unknown scaling mode: %d", mode);
+ return BAD_VALUE;
}
- return SurfaceTextureClient::query(what, value);
+
+ Mutex::Autolock lock(mMutex);
+ mScalingMode = mode;
+ return NO_ERROR;
}
-// ----------------------------------------------------------------------------
+int Surface::setBuffersTransform(int transform)
+{
+ ATRACE_CALL();
+ ALOGV("Surface::setBuffersTransform");
+ Mutex::Autolock lock(mMutex);
+ mTransform = transform;
+ return NO_ERROR;
+}
-status_t Surface::lock(SurfaceInfo* other, Region* inOutDirtyRegion) {
- ANativeWindow_Buffer outBuffer;
+int Surface::setBuffersTimestamp(int64_t timestamp)
+{
+ ALOGV("Surface::setBuffersTimestamp");
+ Mutex::Autolock lock(mMutex);
+ mTimestamp = timestamp;
+ return NO_ERROR;
+}
- ARect temp;
- ARect* inOutDirtyBounds = NULL;
- if (inOutDirtyRegion) {
- temp = inOutDirtyRegion->getBounds();
- inOutDirtyBounds = &temp;
+void Surface::freeAllBuffers() {
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ mSlots[i].buffer = 0;
}
+}
- status_t err = SurfaceTextureClient::lock(&outBuffer, inOutDirtyBounds);
+// ----------------------------------------------------------------------
+// the lock/unlock APIs must be used from the same thread
- if (err == NO_ERROR) {
- other->w = uint32_t(outBuffer.width);
- other->h = uint32_t(outBuffer.height);
- other->s = uint32_t(outBuffer.stride);
- other->usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
- other->format = uint32_t(outBuffer.format);
- other->bits = outBuffer.bits;
+static status_t copyBlt(
+ const sp<GraphicBuffer>& dst,
+ const sp<GraphicBuffer>& src,
+ const Region& reg)
+{
+ // src and dst with, height and format must be identical. no verification
+ // is done here.
+ status_t err;
+ uint8_t const * src_bits = NULL;
+ err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
+ ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
+
+ uint8_t* dst_bits = NULL;
+ err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits);
+ ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
+
+ Region::const_iterator head(reg.begin());
+ Region::const_iterator tail(reg.end());
+ if (head != tail && src_bits && dst_bits) {
+ const size_t bpp = bytesPerPixel(src->format);
+ const size_t dbpr = dst->stride * bpp;
+ const size_t sbpr = src->stride * bpp;
+
+ while (head != tail) {
+ const Rect& r(*head++);
+ ssize_t h = r.height();
+ if (h <= 0) continue;
+ size_t size = r.width() * 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);
+ }
}
- if (inOutDirtyRegion) {
- inOutDirtyRegion->set( static_cast<Rect const&>(temp) );
+ if (src_bits)
+ src->unlock();
+
+ if (dst_bits)
+ dst->unlock();
+
+ return err;
+}
+
+// ----------------------------------------------------------------------------
+
+status_t Surface::lock(
+ ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
+{
+ if (mLockedBuffer != 0) {
+ ALOGE("Surface::lock failed, already locked");
+ return INVALID_OPERATION;
}
+ if (!mConnectedToCpu) {
+ int err = Surface::connect(NATIVE_WINDOW_API_CPU);
+ if (err) {
+ return err;
+ }
+ // we're intending to do software rendering from this point
+ setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ }
+
+ ANativeWindowBuffer* out;
+ int fenceFd = -1;
+ status_t err = dequeueBuffer(&out, &fenceFd);
+ ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
+ if (err == NO_ERROR) {
+ sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
+ sp<Fence> fence(new Fence(fenceFd));
+
+ err = fence->waitForever("Surface::lock");
+ if (err != OK) {
+ ALOGE("Fence::wait failed (%s)", strerror(-err));
+ cancelBuffer(out, fenceFd);
+ return err;
+ }
+
+ const Rect bounds(backBuffer->width, backBuffer->height);
+
+ Region newDirtyRegion;
+ if (inOutDirtyBounds) {
+ newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
+ newDirtyRegion.andSelf(bounds);
+ } else {
+ newDirtyRegion.set(bounds);
+ }
+
+ // figure out if we can copy the frontbuffer back
+ const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
+ const bool canCopyBack = (frontBuffer != 0 &&
+ backBuffer->width == frontBuffer->width &&
+ backBuffer->height == frontBuffer->height &&
+ backBuffer->format == frontBuffer->format);
+
+ if (canCopyBack) {
+ // copy the area that is invalid and not repainted this round
+ const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
+ if (!copyback.isEmpty())
+ copyBlt(backBuffer, frontBuffer, copyback);
+ } else {
+ // if we can't copy-back anything, modify the user's dirty
+ // region to make sure they redraw the whole buffer
+ newDirtyRegion.set(bounds);
+ mDirtyRegion.clear();
+ Mutex::Autolock lock(mMutex);
+ for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
+ mSlots[i].dirtyRegion.clear();
+ }
+ }
+
+
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
+ if (backBufferSlot >= 0) {
+ Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
+ mDirtyRegion.subtract(dirtyRegion);
+ dirtyRegion = newDirtyRegion;
+ }
+ }
+
+ mDirtyRegion.orSelf(newDirtyRegion);
+ if (inOutDirtyBounds) {
+ *inOutDirtyBounds = newDirtyRegion.getBounds();
+ }
+
+ void* vaddr;
+ status_t res = backBuffer->lock(
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ newDirtyRegion.bounds(), &vaddr);
+
+ ALOGW_IF(res, "failed locking buffer (handle = %p)",
+ backBuffer->handle);
+
+ if (res != 0) {
+ err = INVALID_OPERATION;
+ } else {
+ mLockedBuffer = backBuffer;
+ outBuffer->width = backBuffer->width;
+ outBuffer->height = backBuffer->height;
+ outBuffer->stride = backBuffer->stride;
+ outBuffer->format = backBuffer->format;
+ outBuffer->bits = vaddr;
+ }
+ }
return err;
}
-status_t Surface::unlockAndPost() {
- return SurfaceTextureClient::unlockAndPost();
+status_t Surface::unlockAndPost()
+{
+ if (mLockedBuffer == 0) {
+ ALOGE("Surface::unlockAndPost failed, no locked buffer");
+ return INVALID_OPERATION;
+ }
+
+ status_t err = mLockedBuffer->unlock();
+ ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
+
+ err = queueBuffer(mLockedBuffer.get(), -1);
+ ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
+ mLockedBuffer->handle, strerror(-err));
+
+ mPostedBuffer = mLockedBuffer;
+ mLockedBuffer = 0;
+ return err;
}
-// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 80dd6ee..f345df8 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -31,7 +31,8 @@
#include <ui/DisplayInfo.h>
-#include <gui/ISurface.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/SurfaceComposerClient.h>
@@ -115,18 +116,20 @@ class Composer : public Singleton<Composer>
SortedVector<ComposerState> mComposerStates;
SortedVector<DisplayState > mDisplayStates;
uint32_t mForceSynchronous;
+ uint32_t mTransactionNestCount;
bool mAnimation;
Composer() : Singleton<Composer>(),
- mForceSynchronous(0),
+ mForceSynchronous(0), mTransactionNestCount(0),
mAnimation(false)
{ }
+ void openGlobalTransactionImpl();
void closeGlobalTransactionImpl(bool synchronous);
void setAnimationTransactionImpl();
layer_state_t* getLayerStateLocked(
- const sp<SurfaceComposerClient>& client, SurfaceID id);
+ const sp<SurfaceComposerClient>& client, const sp<IBinder>& id);
DisplayState& getDisplayStateLocked(const sp<IBinder>& token);
@@ -134,28 +137,29 @@ public:
sp<IBinder> createDisplay(const String8& displayName, bool secure);
sp<IBinder> getBuiltInDisplay(int32_t id);
- status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setPosition(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float x, float y);
- status_t setSize(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setSize(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
uint32_t w, uint32_t h);
- status_t setLayer(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
int32_t z);
- status_t setFlags(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(
- const sp<SurfaceComposerClient>& client, SurfaceID id,
+ const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const Region& transparentRegion);
- status_t setAlpha(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float alpha);
- status_t setMatrix(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float dsdx, float dtdx, float dsdy, float dtdy);
status_t setOrientation(int orientation);
- status_t setCrop(const sp<SurfaceComposerClient>& client, SurfaceID id,
+ status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const Rect& crop);
status_t setLayerStack(const sp<SurfaceComposerClient>& client,
- SurfaceID id, uint32_t layerStack);
+ const sp<IBinder>& id, uint32_t layerStack);
- void setDisplaySurface(const sp<IBinder>& token, const sp<ISurfaceTexture>& surface);
+ void setDisplaySurface(const sp<IBinder>& token,
+ const sp<IGraphicBufferProducer>& bufferProducer);
void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
void setDisplayProjection(const sp<IBinder>& token,
uint32_t orientation,
@@ -166,6 +170,10 @@ public:
Composer::getInstance().setAnimationTransactionImpl();
}
+ static void openGlobalTransaction() {
+ Composer::getInstance().openGlobalTransactionImpl();
+ }
+
static void closeGlobalTransaction(bool synchronous) {
Composer::getInstance().closeGlobalTransactionImpl(synchronous);
}
@@ -184,6 +192,13 @@ sp<IBinder> Composer::getBuiltInDisplay(int32_t id) {
return ComposerService::getComposerService()->getBuiltInDisplay(id);
}
+void Composer::openGlobalTransactionImpl() {
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ mTransactionNestCount += 1;
+ }
+}
+
void Composer::closeGlobalTransactionImpl(bool synchronous) {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
@@ -193,13 +208,21 @@ void Composer::closeGlobalTransactionImpl(bool synchronous) {
{ // scope for the lock
Mutex::Autolock _l(mLock);
+ mForceSynchronous |= synchronous;
+ if (!mTransactionNestCount) {
+ ALOGW("At least one call to closeGlobalTransaction() was not matched by a prior "
+ "call to openGlobalTransaction().");
+ } else if (--mTransactionNestCount) {
+ return;
+ }
+
transaction = mComposerStates;
mComposerStates.clear();
displayTransaction = mDisplayStates;
mDisplayStates.clear();
- if (synchronous || mForceSynchronous) {
+ if (mForceSynchronous) {
flags |= ISurfaceComposer::eSynchronous;
}
if (mAnimation) {
@@ -219,7 +242,7 @@ void Composer::setAnimationTransactionImpl() {
}
layer_state_t* Composer::getLayerStateLocked(
- const sp<SurfaceComposerClient>& client, SurfaceID id) {
+ const sp<SurfaceComposerClient>& client, const sp<IBinder>& id) {
ComposerState s;
s.client = client->mClient;
@@ -236,7 +259,7 @@ layer_state_t* Composer::getLayerStateLocked(
}
status_t Composer::setPosition(const sp<SurfaceComposerClient>& client,
- SurfaceID id, float x, float y) {
+ const sp<IBinder>& id, float x, float y) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -248,7 +271,7 @@ status_t Composer::setPosition(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setSize(const sp<SurfaceComposerClient>& client,
- SurfaceID id, uint32_t w, uint32_t h) {
+ const sp<IBinder>& id, uint32_t w, uint32_t h) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -264,7 +287,7 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
- SurfaceID id, int32_t z) {
+ const sp<IBinder>& id, int32_t z) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -275,7 +298,7 @@ status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setFlags(const sp<SurfaceComposerClient>& client,
- SurfaceID id, uint32_t flags,
+ const sp<IBinder>& id, uint32_t flags,
uint32_t mask) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
@@ -289,7 +312,7 @@ status_t Composer::setFlags(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setTransparentRegionHint(
- const sp<SurfaceComposerClient>& client, SurfaceID id,
+ const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const Region& transparentRegion) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
@@ -301,7 +324,7 @@ status_t Composer::setTransparentRegionHint(
}
status_t Composer::setAlpha(const sp<SurfaceComposerClient>& client,
- SurfaceID id, float alpha) {
+ const sp<IBinder>& id, float alpha) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -312,7 +335,7 @@ status_t Composer::setAlpha(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client,
- SurfaceID id, uint32_t layerStack) {
+ const sp<IBinder>& id, uint32_t layerStack) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -323,7 +346,7 @@ status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
- SurfaceID id, float dsdx, float dtdx,
+ const sp<IBinder>& id, float dsdx, float dtdx,
float dsdy, float dtdy) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
@@ -340,7 +363,7 @@ status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setCrop(const sp<SurfaceComposerClient>& client,
- SurfaceID id, const Rect& crop) {
+ const sp<IBinder>& id, const Rect& crop) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -365,10 +388,10 @@ DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) {
}
void Composer::setDisplaySurface(const sp<IBinder>& token,
- const sp<ISurfaceTexture>& surface) {
+ const sp<IGraphicBufferProducer>& bufferProducer) {
Mutex::Autolock _l(mLock);
DisplayState& s(getDisplayStateLocked(token));
- s.surface = surface;
+ s.surface = bufferProducer;
s.what |= DisplayState::eSurfaceChanged;
}
@@ -448,16 +471,18 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface(
PixelFormat format,
uint32_t flags)
{
- sp<SurfaceControl> result;
+ sp<SurfaceControl> sur;
if (mStatus == NO_ERROR) {
- ISurfaceComposerClient::surface_data_t data;
- sp<ISurface> surface = mClient->createSurface(&data, name,
- w, h, format, flags);
- if (surface != 0) {
- result = new SurfaceControl(this, surface, data);
+ sp<IBinder> handle;
+ sp<IGraphicBufferProducer> gbp;
+ status_t err = mClient->createSurface(name, w, h, format, flags,
+ &handle, &gbp);
+ ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
+ if (err == NO_ERROR) {
+ sur = new SurfaceControl(this, handle, gbp);
}
}
- return result;
+ return sur;
}
sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName,
@@ -469,7 +494,7 @@ sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) {
return Composer::getInstance().getBuiltInDisplay(id);
}
-status_t SurfaceComposerClient::destroySurface(SurfaceID sid) {
+status_t SurfaceComposerClient::destroySurface(const sp<IBinder>& sid) {
if (mStatus != NO_ERROR)
return mStatus;
status_t err = mClient->destroySurface(sid);
@@ -483,7 +508,7 @@ inline Composer& SurfaceComposerClient::getComposer() {
// ----------------------------------------------------------------------------
void SurfaceComposerClient::openGlobalTransaction() {
- // Currently a no-op
+ Composer::openGlobalTransaction();
}
void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) {
@@ -496,53 +521,53 @@ void SurfaceComposerClient::setAnimationTransaction() {
// ----------------------------------------------------------------------------
-status_t SurfaceComposerClient::setCrop(SurfaceID id, const Rect& crop) {
+status_t SurfaceComposerClient::setCrop(const sp<IBinder>& id, const Rect& crop) {
return getComposer().setCrop(this, id, crop);
}
-status_t SurfaceComposerClient::setPosition(SurfaceID id, float x, float y) {
+status_t SurfaceComposerClient::setPosition(const sp<IBinder>& id, float x, float y) {
return getComposer().setPosition(this, id, x, y);
}
-status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) {
+status_t SurfaceComposerClient::setSize(const sp<IBinder>& id, uint32_t w, uint32_t h) {
return getComposer().setSize(this, id, w, h);
}
-status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) {
+status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, int32_t z) {
return getComposer().setLayer(this, id, z);
}
-status_t SurfaceComposerClient::hide(SurfaceID id) {
+status_t SurfaceComposerClient::hide(const sp<IBinder>& id) {
return getComposer().setFlags(this, id,
layer_state_t::eLayerHidden,
layer_state_t::eLayerHidden);
}
-status_t SurfaceComposerClient::show(SurfaceID id) {
+status_t SurfaceComposerClient::show(const sp<IBinder>& id) {
return getComposer().setFlags(this, id,
0,
layer_state_t::eLayerHidden);
}
-status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags,
+status_t SurfaceComposerClient::setFlags(const sp<IBinder>& id, uint32_t flags,
uint32_t mask) {
return getComposer().setFlags(this, id, flags, mask);
}
-status_t SurfaceComposerClient::setTransparentRegionHint(SurfaceID id,
+status_t SurfaceComposerClient::setTransparentRegionHint(const sp<IBinder>& id,
const Region& transparentRegion) {
return getComposer().setTransparentRegionHint(this, id, transparentRegion);
}
-status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) {
+status_t SurfaceComposerClient::setAlpha(const sp<IBinder>& id, float alpha) {
return getComposer().setAlpha(this, id, alpha);
}
-status_t SurfaceComposerClient::setLayerStack(SurfaceID id, uint32_t layerStack) {
+status_t SurfaceComposerClient::setLayerStack(const sp<IBinder>& id, uint32_t layerStack) {
return getComposer().setLayerStack(this, id, layerStack);
}
-status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx,
+status_t SurfaceComposerClient::setMatrix(const sp<IBinder>& id, float dsdx, float dtdx,
float dsdy, float dtdy) {
return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
}
@@ -550,8 +575,8 @@ status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx,
// ----------------------------------------------------------------------------
void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
- const sp<ISurfaceTexture>& surface) {
- Composer::getInstance().setDisplaySurface(token, surface);
+ const sp<IGraphicBufferProducer>& bufferProducer) {
+ Composer::getInstance().setDisplaySurface(token, bufferProducer);
}
void SurfaceComposerClient::setDisplayLayerStack(const sp<IBinder>& token,
@@ -585,27 +610,33 @@ void SurfaceComposerClient::unblankDisplay(const sp<IBinder>& token) {
// ----------------------------------------------------------------------------
+status_t ScreenshotClient::capture(
+ const sp<IBinder>& display,
+ const sp<IGraphicBufferProducer>& producer,
+ uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ return s->captureScreen(display, producer,
+ reqWidth, reqHeight, minLayerZ, maxLayerZ,
+ false);
+}
+
ScreenshotClient::ScreenshotClient()
- : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
+ : mHaveBuffer(false) {
+ memset(&mBuffer, 0, sizeof(mBuffer));
}
-status_t ScreenshotClient::update(const sp<IBinder>& display) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == NULL) return NO_INIT;
- mHeap = 0;
- return s->captureScreen(display, &mHeap,
- &mWidth, &mHeight, &mFormat, 0, 0,
- 0, -1UL);
+ScreenshotClient::~ScreenshotClient() {
+ ScreenshotClient::release();
}
-status_t ScreenshotClient::update(const sp<IBinder>& display,
- uint32_t reqWidth, uint32_t reqHeight) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == NULL) return NO_INIT;
- mHeap = 0;
- return s->captureScreen(display, &mHeap,
- &mWidth, &mHeight, &mFormat, reqWidth, reqHeight,
- 0, -1UL);
+sp<CpuConsumer> ScreenshotClient::getCpuConsumer() const {
+ if (mCpuConsumer == NULL) {
+ mCpuConsumer = new CpuConsumer(1);
+ mCpuConsumer->setName(String8("ScreenshotClient"));
+ }
+ return mCpuConsumer;
}
status_t ScreenshotClient::update(const sp<IBinder>& display,
@@ -613,38 +644,66 @@ status_t ScreenshotClient::update(const sp<IBinder>& display,
uint32_t minLayerZ, uint32_t maxLayerZ) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
- mHeap = 0;
- return s->captureScreen(display, &mHeap,
- &mWidth, &mHeight, &mFormat, reqWidth, reqHeight,
- minLayerZ, maxLayerZ);
+ sp<CpuConsumer> cpuConsumer = getCpuConsumer();
+
+ if (mHaveBuffer) {
+ mCpuConsumer->unlockBuffer(mBuffer);
+ memset(&mBuffer, 0, sizeof(mBuffer));
+ mHaveBuffer = false;
+ }
+
+ status_t err = s->captureScreen(display,cpuConsumer->getBufferQueue(),
+ reqWidth, reqHeight, minLayerZ, maxLayerZ, true);
+
+ if (err == NO_ERROR) {
+ err = mCpuConsumer->lockNextBuffer(&mBuffer);
+ if (err == NO_ERROR) {
+ mHaveBuffer = true;
+ }
+ }
+ return err;
+}
+
+status_t ScreenshotClient::update(const sp<IBinder>& display) {
+ return ScreenshotClient::update(display, 0, 0, 0, -1UL);
+}
+
+status_t ScreenshotClient::update(const sp<IBinder>& display,
+ uint32_t reqWidth, uint32_t reqHeight) {
+ return ScreenshotClient::update(display, reqWidth, reqHeight, 0, -1UL);
}
void ScreenshotClient::release() {
- mHeap = 0;
+ if (mHaveBuffer) {
+ mCpuConsumer->unlockBuffer(mBuffer);
+ memset(&mBuffer, 0, sizeof(mBuffer));
+ mHaveBuffer = false;
+ }
+ mCpuConsumer.clear();
}
void const* ScreenshotClient::getPixels() const {
- return mHeap->getBase();
+ return mBuffer.data;
}
uint32_t ScreenshotClient::getWidth() const {
- return mWidth;
+ return mBuffer.width;
}
uint32_t ScreenshotClient::getHeight() const {
- return mHeight;
+ return mBuffer.height;
}
PixelFormat ScreenshotClient::getFormat() const {
- return mFormat;
+ return mBuffer.format;
}
uint32_t ScreenshotClient::getStride() const {
- return mWidth;
+ return mBuffer.stride;
}
size_t ScreenshotClient::getSize() const {
- return mHeap->getSize();
+ return mBuffer.stride * mBuffer.height * bytesPerPixel(mBuffer.format);
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
new file mode 100644
index 0000000..f4e88f5
--- /dev/null
+++ b/libs/gui/SurfaceControl.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2007 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 LOG_TAG "SurfaceControl"
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <android/native_window.h>
+
+#include <utils/CallStack.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+namespace android {
+
+// ============================================================================
+// SurfaceControl
+// ============================================================================
+
+SurfaceControl::SurfaceControl(
+ const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& handle,
+ const sp<IGraphicBufferProducer>& gbp)
+ : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp)
+{
+}
+
+SurfaceControl::~SurfaceControl()
+{
+ destroy();
+}
+
+void SurfaceControl::destroy()
+{
+ if (isValid()) {
+ mClient->destroySurface(mHandle);
+ }
+ // clear all references and trigger an IPC now, to make sure things
+ // happen without delay, since these resources are quite heavy.
+ mClient.clear();
+ mHandle.clear();
+ mGraphicBufferProducer.clear();
+ IPCThreadState::self()->flushCommands();
+}
+
+void SurfaceControl::clear()
+{
+ // here, the window manager tells us explicitly that we should destroy
+ // the surface's resource. Soon after this call, it will also release
+ // its last reference (which will call the dtor); however, it is possible
+ // that a client living in the same process still holds references which
+ // would delay the call to the dtor -- that is why we need this explicit
+ // "clear()" call.
+ destroy();
+}
+
+bool SurfaceControl::isSameSurface(
+ const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs)
+{
+ if (lhs == 0 || rhs == 0)
+ return false;
+ return lhs->mHandle == rhs->mHandle;
+}
+
+status_t SurfaceControl::setLayerStack(int32_t layerStack) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setLayerStack(mHandle, layerStack);
+}
+status_t SurfaceControl::setLayer(int32_t layer) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setLayer(mHandle, layer);
+}
+status_t SurfaceControl::setPosition(float x, float y) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setPosition(mHandle, x, y);
+}
+status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setSize(mHandle, w, h);
+}
+status_t SurfaceControl::hide() {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->hide(mHandle);
+}
+status_t SurfaceControl::show() {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->show(mHandle);
+}
+status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setFlags(mHandle, flags, mask);
+}
+status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setTransparentRegionHint(mHandle, transparent);
+}
+status_t SurfaceControl::setAlpha(float alpha) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setAlpha(mHandle, alpha);
+}
+status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setMatrix(mHandle, dsdx, dtdx, dsdy, dtdy);
+}
+status_t SurfaceControl::setCrop(const Rect& crop) {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->setCrop(mHandle, crop);
+}
+
+status_t SurfaceControl::validate() const
+{
+ if (mHandle==0 || mClient==0) {
+ ALOGE("invalid handle (%p) or client (%p)",
+ mHandle.get(), mClient.get());
+ return NO_INIT;
+ }
+ return NO_ERROR;
+}
+
+status_t SurfaceControl::writeSurfaceToParcel(
+ const sp<SurfaceControl>& control, Parcel* parcel)
+{
+ sp<IGraphicBufferProducer> bp;
+ if (control != NULL) {
+ bp = control->mGraphicBufferProducer;
+ }
+ return parcel->writeStrongBinder(bp->asBinder());
+}
+
+sp<Surface> SurfaceControl::getSurface() const
+{
+ Mutex::Autolock _l(mLock);
+ if (mSurfaceData == 0) {
+ mSurfaceData = new Surface(mGraphicBufferProducer);
+ }
+ return mSurfaceData;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
deleted file mode 100644
index afdbf04..0000000
--- a/libs/gui/SurfaceTextureClient.cpp
+++ /dev/null
@@ -1,854 +0,0 @@
-/*
- * Copyright (C) 2010 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 LOG_TAG "SurfaceTextureClient"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include <android/native_window.h>
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-#include <ui/Fence.h>
-
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceTexture.h>
-#include <gui/SurfaceTextureClient.h>
-
-#include <private/gui/ComposerService.h>
-
-namespace android {
-
-SurfaceTextureClient::SurfaceTextureClient(
- const sp<ISurfaceTexture>& surfaceTexture)
-{
- SurfaceTextureClient::init();
- SurfaceTextureClient::setISurfaceTexture(surfaceTexture);
-}
-
-// see SurfaceTextureClient.h
-SurfaceTextureClient::SurfaceTextureClient(const
- sp<SurfaceTexture>& surfaceTexture)
-{
- SurfaceTextureClient::init();
- SurfaceTextureClient::setISurfaceTexture(surfaceTexture->getBufferQueue());
-}
-
-SurfaceTextureClient::SurfaceTextureClient() {
- SurfaceTextureClient::init();
-}
-
-SurfaceTextureClient::~SurfaceTextureClient() {
- if (mConnectedToCpu) {
- SurfaceTextureClient::disconnect(NATIVE_WINDOW_API_CPU);
- }
-}
-
-void SurfaceTextureClient::init() {
- // Initialize the ANativeWindow function pointers.
- ANativeWindow::setSwapInterval = hook_setSwapInterval;
- ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
- ANativeWindow::cancelBuffer = hook_cancelBuffer;
- ANativeWindow::queueBuffer = hook_queueBuffer;
- ANativeWindow::query = hook_query;
- ANativeWindow::perform = hook_perform;
-
- ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
- ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
- ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
- ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
-
- const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
- const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
-
- mReqWidth = 0;
- mReqHeight = 0;
- mReqFormat = 0;
- mReqUsage = 0;
- mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
- mCrop.clear();
- mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
- mTransform = 0;
- mDefaultWidth = 0;
- mDefaultHeight = 0;
- mUserWidth = 0;
- mUserHeight = 0;
- mTransformHint = 0;
- mConsumerRunningBehind = false;
- mConnectedToCpu = false;
-}
-
-void SurfaceTextureClient::setISurfaceTexture(
- const sp<ISurfaceTexture>& surfaceTexture)
-{
- mSurfaceTexture = surfaceTexture;
-}
-
-sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const {
- return mSurfaceTexture;
-}
-
-int SurfaceTextureClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
- SurfaceTextureClient* c = getSelf(window);
- return c->setSwapInterval(interval);
-}
-
-int SurfaceTextureClient::hook_dequeueBuffer(ANativeWindow* window,
- ANativeWindowBuffer** buffer, int* fenceFd) {
- SurfaceTextureClient* c = getSelf(window);
- return c->dequeueBuffer(buffer, fenceFd);
-}
-
-int SurfaceTextureClient::hook_cancelBuffer(ANativeWindow* window,
- ANativeWindowBuffer* buffer, int fenceFd) {
- SurfaceTextureClient* c = getSelf(window);
- return c->cancelBuffer(buffer, fenceFd);
-}
-
-int SurfaceTextureClient::hook_queueBuffer(ANativeWindow* window,
- ANativeWindowBuffer* buffer, int fenceFd) {
- SurfaceTextureClient* c = getSelf(window);
- return c->queueBuffer(buffer, fenceFd);
-}
-
-int SurfaceTextureClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer** buffer) {
- SurfaceTextureClient* c = getSelf(window);
- ANativeWindowBuffer* buf;
- int fenceFd = -1;
- int result = c->dequeueBuffer(&buf, &fenceFd);
- sp<Fence> fence(new Fence(fenceFd));
- int waitResult = fence->waitForever(1000, "dequeueBuffer_DEPRECATED");
- if (waitResult != OK) {
- ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
- waitResult);
- c->cancelBuffer(buf, -1);
- return waitResult;
- }
- *buffer = buf;
- return result;
-}
-
-int SurfaceTextureClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- SurfaceTextureClient* c = getSelf(window);
- return c->cancelBuffer(buffer, -1);
-}
-
-int SurfaceTextureClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- SurfaceTextureClient* c = getSelf(window);
- return c->lockBuffer_DEPRECATED(buffer);
-}
-
-int SurfaceTextureClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- SurfaceTextureClient* c = getSelf(window);
- return c->queueBuffer(buffer, -1);
-}
-
-int SurfaceTextureClient::hook_query(const ANativeWindow* window,
- int what, int* value) {
- const SurfaceTextureClient* c = getSelf(window);
- return c->query(what, value);
-}
-
-int SurfaceTextureClient::hook_perform(ANativeWindow* window, int operation, ...) {
- va_list args;
- va_start(args, operation);
- SurfaceTextureClient* c = getSelf(window);
- return c->perform(operation, args);
-}
-
-int SurfaceTextureClient::setSwapInterval(int interval) {
- ATRACE_CALL();
- // EGL specification states:
- // interval is silently clamped to minimum and maximum implementation
- // dependent values before being stored.
- // Although we don't have to, we apply the same logic here.
-
- if (interval < minSwapInterval)
- interval = minSwapInterval;
-
- if (interval > maxSwapInterval)
- interval = maxSwapInterval;
-
- status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false);
-
- return res;
-}
-
-int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer,
- int* fenceFd) {
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::dequeueBuffer");
- Mutex::Autolock lock(mMutex);
- int buf = -1;
- int reqW = mReqWidth ? mReqWidth : mUserWidth;
- int reqH = mReqHeight ? mReqHeight : mUserHeight;
- sp<Fence> fence;
- status_t result = mSurfaceTexture->dequeueBuffer(&buf, fence, reqW, reqH,
- mReqFormat, mReqUsage);
- if (result < 0) {
- ALOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
- "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
- result);
- return result;
- }
- sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
- if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
- freeAllBuffers();
- }
-
- if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
- result = mSurfaceTexture->requestBuffer(buf, &gbuf);
- if (result != NO_ERROR) {
- ALOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d",
- result);
- return result;
- }
- }
-
- if (fence.get()) {
- *fenceFd = fence->dup();
- if (*fenceFd == -1) {
- ALOGE("dequeueBuffer: error duping fence: %d", errno);
- // dup() should never fail; something is badly wrong. Soldier on
- // and hope for the best; the worst that should happen is some
- // visible corruption that lasts until the next frame.
- }
- } else {
- *fenceFd = -1;
- }
-
- *buffer = gbuf.get();
- return OK;
-}
-
-int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer,
- int fenceFd) {
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::cancelBuffer");
- Mutex::Autolock lock(mMutex);
- int i = getSlotFromBufferLocked(buffer);
- if (i < 0) {
- return i;
- }
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL);
- mSurfaceTexture->cancelBuffer(i, fence);
- return OK;
-}
-
-int SurfaceTextureClient::getSlotFromBufferLocked(
- android_native_buffer_t* buffer) const {
- bool dumpedState = false;
- for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- if (mSlots[i].buffer != NULL &&
- mSlots[i].buffer->handle == buffer->handle) {
- return i;
- }
- }
- ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
- return BAD_VALUE;
-}
-
-int SurfaceTextureClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) {
- ALOGV("SurfaceTextureClient::lockBuffer");
- Mutex::Autolock lock(mMutex);
- return OK;
-}
-
-int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::queueBuffer");
- Mutex::Autolock lock(mMutex);
- int64_t timestamp;
- if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
- timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- ALOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",
- timestamp / 1000000.f);
- } else {
- timestamp = mTimestamp;
- }
- int i = getSlotFromBufferLocked(buffer);
- if (i < 0) {
- return i;
- }
-
-
- // Make sure the crop rectangle is entirely inside the buffer.
- Rect crop;
- mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
-
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL);
- ISurfaceTexture::QueueBufferOutput output;
- ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode,
- mTransform, fence);
- status_t err = mSurfaceTexture->queueBuffer(i, input, &output);
- if (err != OK) {
- ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
- }
- uint32_t numPendingBuffers = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
- &numPendingBuffers);
-
- mConsumerRunningBehind = (numPendingBuffers >= 2);
-
- return err;
-}
-
-int SurfaceTextureClient::query(int what, int* value) const {
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::query");
- { // scope for the lock
- Mutex::Autolock lock(mMutex);
- switch (what) {
- case NATIVE_WINDOW_FORMAT:
- if (mReqFormat) {
- *value = mReqFormat;
- return NO_ERROR;
- }
- break;
- case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
- sp<ISurfaceComposer> composer(
- ComposerService::getComposerService());
- if (composer->authenticateSurfaceTexture(mSurfaceTexture)) {
- *value = 1;
- } else {
- *value = 0;
- }
- return NO_ERROR;
- }
- case NATIVE_WINDOW_CONCRETE_TYPE:
- *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
- return NO_ERROR;
- case NATIVE_WINDOW_DEFAULT_WIDTH:
- *value = mUserWidth ? mUserWidth : mDefaultWidth;
- return NO_ERROR;
- case NATIVE_WINDOW_DEFAULT_HEIGHT:
- *value = mUserHeight ? mUserHeight : mDefaultHeight;
- return NO_ERROR;
- case NATIVE_WINDOW_TRANSFORM_HINT:
- *value = mTransformHint;
- return NO_ERROR;
- case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
- status_t err = NO_ERROR;
- if (!mConsumerRunningBehind) {
- *value = 0;
- } else {
- err = mSurfaceTexture->query(what, value);
- if (err == NO_ERROR) {
- mConsumerRunningBehind = *value;
- }
- }
- return err;
- }
- }
- }
- return mSurfaceTexture->query(what, value);
-}
-
-int SurfaceTextureClient::perform(int operation, va_list args)
-{
- int res = NO_ERROR;
- switch (operation) {
- case NATIVE_WINDOW_CONNECT:
- // deprecated. must return NO_ERROR.
- break;
- case NATIVE_WINDOW_DISCONNECT:
- // deprecated. must return NO_ERROR.
- break;
- case NATIVE_WINDOW_SET_USAGE:
- res = dispatchSetUsage(args);
- break;
- case NATIVE_WINDOW_SET_CROP:
- res = dispatchSetCrop(args);
- break;
- case NATIVE_WINDOW_SET_BUFFER_COUNT:
- res = dispatchSetBufferCount(args);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
- res = dispatchSetBuffersGeometry(args);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
- res = dispatchSetBuffersTransform(args);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
- res = dispatchSetBuffersTimestamp(args);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
- res = dispatchSetBuffersDimensions(args);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
- res = dispatchSetBuffersUserDimensions(args);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
- res = dispatchSetBuffersFormat(args);
- break;
- case NATIVE_WINDOW_LOCK:
- res = dispatchLock(args);
- break;
- case NATIVE_WINDOW_UNLOCK_AND_POST:
- res = dispatchUnlockAndPost(args);
- break;
- case NATIVE_WINDOW_SET_SCALING_MODE:
- res = dispatchSetScalingMode(args);
- break;
- case NATIVE_WINDOW_API_CONNECT:
- res = dispatchConnect(args);
- break;
- case NATIVE_WINDOW_API_DISCONNECT:
- res = dispatchDisconnect(args);
- break;
- default:
- res = NAME_NOT_FOUND;
- break;
- }
- return res;
-}
-
-int SurfaceTextureClient::dispatchConnect(va_list args) {
- int api = va_arg(args, int);
- return connect(api);
-}
-
-int SurfaceTextureClient::dispatchDisconnect(va_list args) {
- int api = va_arg(args, int);
- return disconnect(api);
-}
-
-int SurfaceTextureClient::dispatchSetUsage(va_list args) {
- int usage = va_arg(args, int);
- return setUsage(usage);
-}
-
-int SurfaceTextureClient::dispatchSetCrop(va_list args) {
- android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
- return setCrop(reinterpret_cast<Rect const*>(rect));
-}
-
-int SurfaceTextureClient::dispatchSetBufferCount(va_list args) {
- size_t bufferCount = va_arg(args, size_t);
- return setBufferCount(bufferCount);
-}
-
-int SurfaceTextureClient::dispatchSetBuffersGeometry(va_list args) {
- int w = va_arg(args, int);
- int h = va_arg(args, int);
- int f = va_arg(args, int);
- int err = setBuffersDimensions(w, h);
- if (err != 0) {
- return err;
- }
- return setBuffersFormat(f);
-}
-
-int SurfaceTextureClient::dispatchSetBuffersDimensions(va_list args) {
- int w = va_arg(args, int);
- int h = va_arg(args, int);
- return setBuffersDimensions(w, h);
-}
-
-int SurfaceTextureClient::dispatchSetBuffersUserDimensions(va_list args) {
- int w = va_arg(args, int);
- int h = va_arg(args, int);
- return setBuffersUserDimensions(w, h);
-}
-
-int SurfaceTextureClient::dispatchSetBuffersFormat(va_list args) {
- int f = va_arg(args, int);
- return setBuffersFormat(f);
-}
-
-int SurfaceTextureClient::dispatchSetScalingMode(va_list args) {
- int m = va_arg(args, int);
- return setScalingMode(m);
-}
-
-int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) {
- int transform = va_arg(args, int);
- return setBuffersTransform(transform);
-}
-
-int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
- int64_t timestamp = va_arg(args, int64_t);
- return setBuffersTimestamp(timestamp);
-}
-
-int SurfaceTextureClient::dispatchLock(va_list args) {
- ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
- ARect* inOutDirtyBounds = va_arg(args, ARect*);
- return lock(outBuffer, inOutDirtyBounds);
-}
-
-int SurfaceTextureClient::dispatchUnlockAndPost(va_list args) {
- return unlockAndPost();
-}
-
-
-int SurfaceTextureClient::connect(int api) {
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::connect");
- Mutex::Autolock lock(mMutex);
- ISurfaceTexture::QueueBufferOutput output;
- int err = mSurfaceTexture->connect(api, &output);
- if (err == NO_ERROR) {
- uint32_t numPendingBuffers = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
- &numPendingBuffers);
- mConsumerRunningBehind = (numPendingBuffers >= 2);
- }
- if (!err && api == NATIVE_WINDOW_API_CPU) {
- mConnectedToCpu = true;
- }
- return err;
-}
-
-int SurfaceTextureClient::disconnect(int api) {
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::disconnect");
- Mutex::Autolock lock(mMutex);
- freeAllBuffers();
- int err = mSurfaceTexture->disconnect(api);
- if (!err) {
- mReqFormat = 0;
- mReqWidth = 0;
- mReqHeight = 0;
- mReqUsage = 0;
- mCrop.clear();
- mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
- mTransform = 0;
- if (api == NATIVE_WINDOW_API_CPU) {
- mConnectedToCpu = false;
- }
- }
- return err;
-}
-
-int SurfaceTextureClient::setUsage(uint32_t reqUsage)
-{
- ALOGV("SurfaceTextureClient::setUsage");
- Mutex::Autolock lock(mMutex);
- mReqUsage = reqUsage;
- return OK;
-}
-
-int SurfaceTextureClient::setCrop(Rect const* rect)
-{
- ATRACE_CALL();
-
- Rect realRect;
- if (rect == NULL || rect->isEmpty()) {
- realRect.clear();
- } else {
- realRect = *rect;
- }
-
- ALOGV("SurfaceTextureClient::setCrop rect=[%d %d %d %d]",
- realRect.left, realRect.top, realRect.right, realRect.bottom);
-
- Mutex::Autolock lock(mMutex);
- mCrop = realRect;
- return NO_ERROR;
-}
-
-int SurfaceTextureClient::setBufferCount(int bufferCount)
-{
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::setBufferCount");
- Mutex::Autolock lock(mMutex);
-
- status_t err = mSurfaceTexture->setBufferCount(bufferCount);
- ALOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s",
- bufferCount, strerror(-err));
-
- if (err == NO_ERROR) {
- freeAllBuffers();
- }
-
- return err;
-}
-
-int SurfaceTextureClient::setBuffersDimensions(int w, int h)
-{
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::setBuffersDimensions");
-
- if (w<0 || h<0)
- return BAD_VALUE;
-
- if ((w && !h) || (!w && h))
- return BAD_VALUE;
-
- Mutex::Autolock lock(mMutex);
- mReqWidth = w;
- mReqHeight = h;
- return NO_ERROR;
-}
-
-int SurfaceTextureClient::setBuffersUserDimensions(int w, int h)
-{
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::setBuffersUserDimensions");
-
- if (w<0 || h<0)
- return BAD_VALUE;
-
- if ((w && !h) || (!w && h))
- return BAD_VALUE;
-
- Mutex::Autolock lock(mMutex);
- mUserWidth = w;
- mUserHeight = h;
- return NO_ERROR;
-}
-
-int SurfaceTextureClient::setBuffersFormat(int format)
-{
- ALOGV("SurfaceTextureClient::setBuffersFormat");
-
- if (format<0)
- return BAD_VALUE;
-
- Mutex::Autolock lock(mMutex);
- mReqFormat = format;
- return NO_ERROR;
-}
-
-int SurfaceTextureClient::setScalingMode(int mode)
-{
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::setScalingMode(%d)", mode);
-
- switch (mode) {
- case NATIVE_WINDOW_SCALING_MODE_FREEZE:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
- break;
- default:
- ALOGE("unknown scaling mode: %d", mode);
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mMutex);
- mScalingMode = mode;
- return NO_ERROR;
-}
-
-int SurfaceTextureClient::setBuffersTransform(int transform)
-{
- ATRACE_CALL();
- ALOGV("SurfaceTextureClient::setBuffersTransform");
- Mutex::Autolock lock(mMutex);
- mTransform = transform;
- return NO_ERROR;
-}
-
-int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
-{
- ALOGV("SurfaceTextureClient::setBuffersTimestamp");
- Mutex::Autolock lock(mMutex);
- mTimestamp = timestamp;
- return NO_ERROR;
-}
-
-void SurfaceTextureClient::freeAllBuffers() {
- for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- mSlots[i].buffer = 0;
- }
-}
-
-// ----------------------------------------------------------------------
-// the lock/unlock APIs must be used from the same thread
-
-static status_t copyBlt(
- const sp<GraphicBuffer>& dst,
- const sp<GraphicBuffer>& src,
- const Region& reg)
-{
- // src and dst with, height and format must be identical. no verification
- // is done here.
- status_t err;
- uint8_t const * src_bits = NULL;
- err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
- ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
-
- uint8_t* dst_bits = NULL;
- err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits);
- ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
-
- Region::const_iterator head(reg.begin());
- Region::const_iterator tail(reg.end());
- if (head != tail && src_bits && dst_bits) {
- const size_t bpp = bytesPerPixel(src->format);
- const size_t dbpr = dst->stride * bpp;
- const size_t sbpr = src->stride * bpp;
-
- while (head != tail) {
- const Rect& r(*head++);
- ssize_t h = r.height();
- if (h <= 0) continue;
- size_t size = r.width() * 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);
- }
- }
-
- if (src_bits)
- src->unlock();
-
- if (dst_bits)
- dst->unlock();
-
- return err;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t SurfaceTextureClient::lock(
- ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
-{
- if (mLockedBuffer != 0) {
- ALOGE("Surface::lock failed, already locked");
- return INVALID_OPERATION;
- }
-
- if (!mConnectedToCpu) {
- int err = SurfaceTextureClient::connect(NATIVE_WINDOW_API_CPU);
- if (err) {
- return err;
- }
- // we're intending to do software rendering from this point
- setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
- }
-
- ANativeWindowBuffer* out;
- int fenceFd = -1;
- status_t err = dequeueBuffer(&out, &fenceFd);
- ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
- if (err == NO_ERROR) {
- sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
- sp<Fence> fence(new Fence(fenceFd));
-
- err = fence->waitForever(1000, "SurfaceTextureClient::lock");
- if (err != OK) {
- ALOGE("Fence::wait failed (%s)", strerror(-err));
- cancelBuffer(out, fenceFd);
- return err;
- }
-
- const Rect bounds(backBuffer->width, backBuffer->height);
-
- Region newDirtyRegion;
- if (inOutDirtyBounds) {
- newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
- newDirtyRegion.andSelf(bounds);
- } else {
- newDirtyRegion.set(bounds);
- }
-
- // figure out if we can copy the frontbuffer back
- const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
- const bool canCopyBack = (frontBuffer != 0 &&
- backBuffer->width == frontBuffer->width &&
- backBuffer->height == frontBuffer->height &&
- backBuffer->format == frontBuffer->format);
-
- if (canCopyBack) {
- // copy the area that is invalid and not repainted this round
- const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
- if (!copyback.isEmpty())
- copyBlt(backBuffer, frontBuffer, copyback);
- } else {
- // if we can't copy-back anything, modify the user's dirty
- // region to make sure they redraw the whole buffer
- newDirtyRegion.set(bounds);
- mDirtyRegion.clear();
- Mutex::Autolock lock(mMutex);
- for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
- mSlots[i].dirtyRegion.clear();
- }
- }
-
-
- { // scope for the lock
- Mutex::Autolock lock(mMutex);
- int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
- if (backBufferSlot >= 0) {
- Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
- mDirtyRegion.subtract(dirtyRegion);
- dirtyRegion = newDirtyRegion;
- }
- }
-
- mDirtyRegion.orSelf(newDirtyRegion);
- if (inOutDirtyBounds) {
- *inOutDirtyBounds = newDirtyRegion.getBounds();
- }
-
- void* vaddr;
- status_t res = backBuffer->lock(
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- newDirtyRegion.bounds(), &vaddr);
-
- ALOGW_IF(res, "failed locking buffer (handle = %p)",
- backBuffer->handle);
-
- if (res != 0) {
- err = INVALID_OPERATION;
- } else {
- mLockedBuffer = backBuffer;
- outBuffer->width = backBuffer->width;
- outBuffer->height = backBuffer->height;
- outBuffer->stride = backBuffer->stride;
- outBuffer->format = backBuffer->format;
- outBuffer->bits = vaddr;
- }
- }
- return err;
-}
-
-status_t SurfaceTextureClient::unlockAndPost()
-{
- if (mLockedBuffer == 0) {
- ALOGE("Surface::unlockAndPost failed, no locked buffer");
- return INVALID_OPERATION;
- }
-
- status_t err = mLockedBuffer->unlock();
- ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
-
- err = queueBuffer(mLockedBuffer.get(), -1);
- ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
- mLockedBuffer->handle, strerror(-err));
-
- mPostedBuffer = mLockedBuffer;
- mLockedBuffer = 0;
- return err;
-}
-
-}; // namespace android
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
new file mode 100644
index 0000000..e5804a7
--- /dev/null
+++ b/libs/gui/SyncFeatures.cpp
@@ -0,0 +1,94 @@
+/*
+ ** Copyright 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 LOG_TAG "GLConsumer"
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <utils/Log.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+
+#include <private/gui/SyncFeatures.h>
+
+EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures);
+
+SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(),
+ mHasNativeFenceSync(false),
+ mHasFenceSync(false),
+ mHasWaitSync(false) {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ // This can only be called after EGL has been initialized; otherwise the
+ // check below will abort.
+ const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
+ LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed");
+ if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
+ // This makes GLConsumer use the EGL_ANDROID_native_fence_sync
+ // extension to create Android native fences to signal when all
+ // GLES reads for a given buffer have completed.
+ mHasNativeFenceSync = true;
+ }
+ if (strstr(exts, "EGL_KHR_fence_sync")) {
+ mHasFenceSync = true;
+ }
+ if (strstr(exts, "EGL_KHR_wait_sync")) {
+ mHasWaitSync = true;
+ }
+ mString.append("[using:");
+ if (useNativeFenceSync()) {
+ mString.append(" EGL_ANDROID_native_fence_sync");
+ }
+ if (useFenceSync()) {
+ mString.append(" EGL_KHR_fence_sync");
+ }
+ if (useWaitSync()) {
+ mString.append(" EGL_KHR_wait_sync");
+ }
+ mString.append("]");
+}
+
+bool SyncFeatures::useNativeFenceSync() const {
+ // EGL_ANDROID_native_fence_sync is not compatible with using the
+ // EGL_KHR_fence_sync extension for the same purpose.
+ return mHasNativeFenceSync;
+}
+bool SyncFeatures::useFenceSync() const {
+#ifdef DONT_USE_FENCE_SYNC
+ // on some devices it's better to not use EGL_KHR_fence_sync
+ // even if they have it
+ return false;
+#endif
+ // currently we shall only attempt to use EGL_KHR_fence_sync if
+ // USE_FENCE_SYNC is set in our makefile
+ return !mHasNativeFenceSync && mHasFenceSync;
+}
+bool SyncFeatures::useWaitSync() const {
+ return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync;
+}
+
+String8 SyncFeatures::toString() const {
+ return mString;
+}
+
+} // namespace android
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 4a6f74f..21bd875 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -15,6 +15,7 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
libEGL \
+ libGLESv1_CM \
libGLESv2 \
libbinder \
libcutils \
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 817abb4..62d215b 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -63,28 +63,28 @@ struct DummyConsumer : public BufferQueue::ConsumerListener {
TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
sp<DummyConsumer> dc(new DummyConsumer);
mBQ->consumerConnect(dc);
- ISurfaceTexture::QueueBufferOutput qbo;
+ IGraphicBufferProducer::QueueBufferOutput qbo;
mBQ->connect(NATIVE_WINDOW_API_CPU, &qbo);
mBQ->setBufferCount(4);
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
- ISurfaceTexture::QueueBufferInput qbi(0, Rect(0, 0, 1, 1),
- NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, fence);
+ IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
BufferQueue::BufferItem item;
for (int i = 0; i < 2; i++) {
- ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION,
- mBQ->dequeueBuffer(&slot, fence, 1, 1, 0,
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mBQ->dequeueBuffer(&slot, &fence, 1, 1, 0,
GRALLOC_USAGE_SW_READ_OFTEN));
ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mBQ->acquireBuffer(&item));
}
- ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION,
- mBQ->dequeueBuffer(&slot, fence, 1, 1, 0,
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mBQ->dequeueBuffer(&slot, &fence, 1, 1, 0,
GRALLOC_USAGE_SW_READ_OFTEN));
ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo));
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 72a36bf..73fdd04 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -26,7 +26,7 @@
#include <gtest/gtest.h>
#include <gui/CpuConsumer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
#include <utils/Thread.h>
@@ -35,6 +35,11 @@
#include <ui/FramebufferNativeWindow.h>
+#define CPU_CONSUMER_TEST_FORMAT_RAW 0
+#define CPU_CONSUMER_TEST_FORMAT_Y8 0
+#define CPU_CONSUMER_TEST_FORMAT_Y16 0
+#define CPU_CONSUMER_TEST_FORMAT_RGBA_8888 1
+
namespace android {
struct CpuConsumerTestParams {
@@ -64,7 +69,7 @@ protected:
mCC = new CpuConsumer(params.maxLockedBuffers);
String8 name("CpuConsumer_Under_Test");
mCC->setName(name);
- mSTC = new SurfaceTextureClient(mCC->getProducerInterface());
+ mSTC = new Surface(mCC->getProducerInterface());
mANW = mSTC;
}
@@ -149,7 +154,7 @@ protected:
};
sp<CpuConsumer> mCC;
- sp<SurfaceTextureClient> mSTC;
+ sp<Surface> mSTC;
sp<ANativeWindow> mANW;
};
@@ -157,7 +162,7 @@ protected:
ASSERT_EQ(NO_ERROR, err) << msg << strerror(-err)
void checkPixel(const CpuConsumer::LockedBuffer &buf,
- uint32_t x, uint32_t y, uint32_t r, uint32_t g, uint32_t b) {
+ uint32_t x, uint32_t y, uint32_t r, uint32_t g=0, uint32_t b=0) {
// Ignores components that don't exist for given pixel
switch(buf.format) {
case HAL_PIXEL_FORMAT_RAW_SENSOR: {
@@ -179,6 +184,31 @@ void checkPixel(const CpuConsumer::LockedBuffer &buf,
}
break;
}
+ // ignores g,b
+ case HAL_PIXEL_FORMAT_Y8: {
+ uint8_t *bPtr = (uint8_t*)buf.data;
+ bPtr += y * buf.stride + x;
+ EXPECT_EQ(r, *bPtr) << "at x = " << x << " y = " << y;
+ break;
+ }
+ // ignores g,b
+ case HAL_PIXEL_FORMAT_Y16: {
+ // stride is in pixels, not in bytes
+ uint16_t *bPtr = ((uint16_t*)buf.data) + y * buf.stride + x;
+
+ EXPECT_EQ(r, *bPtr) << "at x = " << x << " y = " << y;
+ break;
+ }
+ case HAL_PIXEL_FORMAT_RGBA_8888: {
+ const int bytesPerPixel = 4;
+ uint8_t *bPtr = (uint8_t*)buf.data;
+ bPtr += (y * buf.stride + x) * bytesPerPixel;
+
+ EXPECT_EQ(r, bPtr[0]) << "at x = " << x << " y = " << y;
+ EXPECT_EQ(g, bPtr[1]) << "at x = " << x << " y = " << y;
+ EXPECT_EQ(b, bPtr[2]) << "at x = " << x << " y = " << y;
+ break;
+ }
default: {
ADD_FAILURE() << "Unknown format for check:" << buf.format;
break;
@@ -189,6 +219,61 @@ void checkPixel(const CpuConsumer::LockedBuffer &buf,
// Fill a YV12 buffer with a multi-colored checkerboard pattern
void fillYV12Buffer(uint8_t* buf, int w, int h, int stride);
+// Fill a Y8/Y16 buffer with a multi-colored checkerboard pattern
+template <typename T> // T == uint8_t or uint16_t
+void fillGreyscaleBuffer(T* buf, int w, int h, int stride, int bpp) {
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int yuvTexOffsetY = 0;
+
+ ASSERT_TRUE(bpp == 8 || bpp == 16);
+ ASSERT_TRUE(sizeof(T)*8 == bpp);
+
+ // stride is in pixels, not in bytes
+ int yuvTexStrideY = stride;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int parityX = (x / blockWidth) & 1;
+ int parityY = (y / blockHeight) & 1;
+ T intensity = (parityX ^ parityY) ? 63 : 191;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+ }
+ }
+}
+
+inline uint8_t chooseColorRgba8888(int blockX, int blockY, uint8_t channel) {
+ const int colorVariations = 3;
+ uint8_t color = ((blockX % colorVariations) + (blockY % colorVariations))
+ % (colorVariations) == channel ? 191: 63;
+
+ return color;
+}
+
+// Fill a RGBA8888 buffer with a multi-colored checkerboard pattern
+void fillRgba8888Buffer(uint8_t* buf, int w, int h, int stride)
+{
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int bytesPerPixel = 4;
+
+ // stride is in pixels, not in bytes
+ for (int x = 0; x < w; ++x) {
+ for (int y = 0; y < h; ++y) {
+ int blockX = (x / blockWidth);
+ int blockY = (y / blockHeight);
+
+ uint8_t r = chooseColorRgba8888(blockX, blockY, 0);
+ uint8_t g = chooseColorRgba8888(blockX, blockY, 1);
+ uint8_t b = chooseColorRgba8888(blockX, blockY, 2);
+
+ buf[(y*stride + x)*bytesPerPixel + 0] = r;
+ buf[(y*stride + x)*bytesPerPixel + 1] = g;
+ buf[(y*stride + x)*bytesPerPixel + 2] = b;
+ buf[(y*stride + x)*bytesPerPixel + 3] = 255;
+ }
+ }
+}
+
// Fill a RAW sensor buffer with a multi-colored checkerboard pattern.
// Assumes GRBG mosaic ordering. Result should be a grid in a 2x2 pattern
// of [ R, B; G, W]
@@ -217,6 +302,89 @@ void fillBayerRawBuffer(uint8_t* buf, int w, int h, int stride) {
}
+template<typename T> // uint8_t or uint16_t
+void checkGreyscaleBuffer(const CpuConsumer::LockedBuffer &buf) {
+ uint32_t w = buf.width;
+ uint32_t h = buf.height;
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int blockRows = h / blockHeight;
+ const int blockCols = w / blockWidth;
+
+ // Top-left square is bright
+ checkPixel(buf, 0, 0, 191);
+ checkPixel(buf, 1, 0, 191);
+ checkPixel(buf, 0, 1, 191);
+ checkPixel(buf, 1, 1, 191);
+
+ // One-right square is dark
+ checkPixel(buf, blockWidth, 0, 63);
+ checkPixel(buf, blockWidth + 1, 0, 63);
+ checkPixel(buf, blockWidth, 1, 63);
+ checkPixel(buf, blockWidth + 1, 1, 63);
+
+ // One-down square is dark
+ checkPixel(buf, 0, blockHeight, 63);
+ checkPixel(buf, 1, blockHeight, 63);
+ checkPixel(buf, 0, blockHeight + 1, 63);
+ checkPixel(buf, 1, blockHeight + 1, 63);
+
+ // One-diag square is bright
+ checkPixel(buf, blockWidth, blockHeight, 191);
+ checkPixel(buf, blockWidth + 1, blockHeight, 191);
+ checkPixel(buf, blockWidth, blockHeight + 1, 191);
+ checkPixel(buf, blockWidth + 1, blockHeight + 1, 191);
+
+ // Test bottom-right pixel
+ const int maxBlockX = ((w-1 + (blockWidth-1)) / blockWidth) & 0x1;
+ const int maxBlockY = ((h-1 + (blockHeight-1)) / blockHeight) & 0x1;
+ uint32_t pixelValue = ((maxBlockX % 2) == (maxBlockY % 2)) ? 191 : 63;
+ checkPixel(buf, w-1, h-1, pixelValue);
+}
+
+void checkRgba8888Buffer(const CpuConsumer::LockedBuffer &buf) {
+ uint32_t w = buf.width;
+ uint32_t h = buf.height;
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int blockRows = h / blockHeight;
+ const int blockCols = w / blockWidth;
+
+ // Top-left square is bright red
+ checkPixel(buf, 0, 0, 191, 63, 63);
+ checkPixel(buf, 1, 0, 191, 63, 63);
+ checkPixel(buf, 0, 1, 191, 63, 63);
+ checkPixel(buf, 1, 1, 191, 63, 63);
+
+ // One-right square is bright green
+ checkPixel(buf, blockWidth, 0, 63, 191, 63);
+ checkPixel(buf, blockWidth + 1, 0, 63, 191, 63);
+ checkPixel(buf, blockWidth, 1, 63, 191, 63);
+ checkPixel(buf, blockWidth + 1, 1, 63, 191, 63);
+
+ // One-down square is bright green
+ checkPixel(buf, 0, blockHeight, 63, 191, 63);
+ checkPixel(buf, 1, blockHeight, 63, 191, 63);
+ checkPixel(buf, 0, blockHeight + 1, 63, 191, 63);
+ checkPixel(buf, 1, blockHeight + 1, 63, 191, 63);
+
+ // One-diag square is bright blue
+ checkPixel(buf, blockWidth, blockHeight, 63, 63, 191);
+ checkPixel(buf, blockWidth + 1, blockHeight, 63, 63, 191);
+ checkPixel(buf, blockWidth, blockHeight + 1, 63, 63, 191);
+ checkPixel(buf, blockWidth + 1, blockHeight + 1, 63, 63, 191);
+
+ // Test bottom-right pixel
+ {
+ const int maxBlockX = ((w-1) / blockWidth);
+ const int maxBlockY = ((h-1) / blockHeight);
+ uint8_t r = chooseColorRgba8888(maxBlockX, maxBlockY, 0);
+ uint8_t g = chooseColorRgba8888(maxBlockX, maxBlockY, 1);
+ uint8_t b = chooseColorRgba8888(maxBlockX, maxBlockY, 2);
+ checkPixel(buf, w-1, h-1, r, g, b);
+ }
+}
+
void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) {
uint32_t w = buf.width;
uint32_t h = buf.height;
@@ -258,6 +426,23 @@ void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) {
checkPixel(buf, w-1, h-1, maxR, maxG, maxB);
}
+void checkAnyBuffer(const CpuConsumer::LockedBuffer &buf, int format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ checkBayerRawBuffer(buf);
+ break;
+ case HAL_PIXEL_FORMAT_Y8:
+ checkGreyscaleBuffer<uint8_t>(buf);
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ checkGreyscaleBuffer<uint16_t>(buf);
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ checkRgba8888Buffer(buf);
+ break;
+ }
+}
+
void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride,
const android_native_rect_t& rect);
@@ -322,6 +507,18 @@ void produceOneFrame(const sp<ANativeWindow>& anw,
case HAL_PIXEL_FORMAT_RAW_SENSOR:
fillBayerRawBuffer(img, params.width, params.height, buf->getStride());
break;
+ case HAL_PIXEL_FORMAT_Y8:
+ fillGreyscaleBuffer<uint8_t>(img, params.width, params.height,
+ buf->getStride(), /*bpp*/8);
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ fillGreyscaleBuffer<uint16_t>((uint16_t*)img, params.width,
+ params.height, buf->getStride(),
+ /*bpp*/16);
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ fillRgba8888Buffer(img, params.width, params.height, buf->getStride());
+ break;
default:
FAIL() << "Unknown pixel format under test!";
break;
@@ -341,7 +538,7 @@ void produceOneFrame(const sp<ANativeWindow>& anw,
// This test is disabled because the HAL_PIXEL_FORMAT_RAW_SENSOR format is not
// supported on all devices.
-TEST_P(CpuConsumerTest, DISABLED_FromCpuSingle) {
+TEST_P(CpuConsumerTest, FromCpuSingle) {
status_t err;
CpuConsumerTestParams params = GetParam();
@@ -369,13 +566,13 @@ TEST_P(CpuConsumerTest, DISABLED_FromCpuSingle) {
EXPECT_EQ(stride, b.stride);
EXPECT_EQ(time, b.timestamp);
- checkBayerRawBuffer(b);
+ checkAnyBuffer(b, GetParam().format);
mCC->unlockBuffer(b);
}
// This test is disabled because the HAL_PIXEL_FORMAT_RAW_SENSOR format is not
// supported on all devices.
-TEST_P(CpuConsumerTest, DISABLED_FromCpuManyInQueue) {
+TEST_P(CpuConsumerTest, FromCpuManyInQueue) {
status_t err;
CpuConsumerTestParams params = GetParam();
@@ -410,7 +607,7 @@ TEST_P(CpuConsumerTest, DISABLED_FromCpuManyInQueue) {
EXPECT_EQ(stride[i], b.stride);
EXPECT_EQ(time[i], b.timestamp);
- checkBayerRawBuffer(b);
+ checkAnyBuffer(b, GetParam().format);
mCC->unlockBuffer(b);
}
@@ -418,7 +615,7 @@ TEST_P(CpuConsumerTest, DISABLED_FromCpuManyInQueue) {
// This test is disabled because the HAL_PIXEL_FORMAT_RAW_SENSOR format is not
// supported on all devices.
-TEST_P(CpuConsumerTest, DISABLED_FromCpuLockMax) {
+TEST_P(CpuConsumerTest, FromCpuLockMax) {
status_t err;
CpuConsumerTestParams params = GetParam();
@@ -452,7 +649,7 @@ TEST_P(CpuConsumerTest, DISABLED_FromCpuLockMax) {
EXPECT_EQ(stride, b[i].stride);
EXPECT_EQ(time, b[i].timestamp);
- checkBayerRawBuffer(b[i]);
+ checkAnyBuffer(b[i], GetParam().format);
}
ALOGV("Locking frame %d (too many)", params.maxLockedBuffers);
@@ -475,7 +672,7 @@ TEST_P(CpuConsumerTest, DISABLED_FromCpuLockMax) {
EXPECT_EQ(stride, bTooMuch.stride);
EXPECT_EQ(time, bTooMuch.timestamp);
- checkBayerRawBuffer(bTooMuch);
+ checkAnyBuffer(bTooMuch, GetParam().format);
ALOGV("Unlocking extra buffer");
err = mCC->unlockBuffer(bTooMuch);
@@ -493,17 +690,66 @@ TEST_P(CpuConsumerTest, DISABLED_FromCpuLockMax) {
}
+CpuConsumerTestParams y8TestSets[] = {
+ { 512, 512, 1, HAL_PIXEL_FORMAT_Y8},
+ { 512, 512, 3, HAL_PIXEL_FORMAT_Y8},
+ { 2608, 1960, 1, HAL_PIXEL_FORMAT_Y8},
+ { 2608, 1960, 3, HAL_PIXEL_FORMAT_Y8},
+ { 100, 100, 1, HAL_PIXEL_FORMAT_Y8},
+ { 100, 100, 3, HAL_PIXEL_FORMAT_Y8},
+};
+
+CpuConsumerTestParams y16TestSets[] = {
+ { 512, 512, 1, HAL_PIXEL_FORMAT_Y16},
+ { 512, 512, 3, HAL_PIXEL_FORMAT_Y16},
+ { 2608, 1960, 1, HAL_PIXEL_FORMAT_Y16},
+ { 2608, 1960, 3, HAL_PIXEL_FORMAT_Y16},
+ { 100, 100, 1, HAL_PIXEL_FORMAT_Y16},
+ { 100, 100, 3, HAL_PIXEL_FORMAT_Y16},
+};
+
CpuConsumerTestParams rawTestSets[] = {
{ 512, 512, 1, HAL_PIXEL_FORMAT_RAW_SENSOR},
{ 512, 512, 3, HAL_PIXEL_FORMAT_RAW_SENSOR},
{ 2608, 1960, 1, HAL_PIXEL_FORMAT_RAW_SENSOR},
{ 2608, 1960, 3, HAL_PIXEL_FORMAT_RAW_SENSOR},
{ 100, 100, 1, HAL_PIXEL_FORMAT_RAW_SENSOR},
- { 100, 100, 3, HAL_PIXEL_FORMAT_RAW_SENSOR}
+ { 100, 100, 3, HAL_PIXEL_FORMAT_RAW_SENSOR},
+};
+
+CpuConsumerTestParams rgba8888TestSets[] = {
+ { 512, 512, 1, HAL_PIXEL_FORMAT_RGBA_8888},
+ { 512, 512, 3, HAL_PIXEL_FORMAT_RGBA_8888},
+ { 2608, 1960, 1, HAL_PIXEL_FORMAT_RGBA_8888},
+ { 2608, 1960, 3, HAL_PIXEL_FORMAT_RGBA_8888},
+ { 100, 100, 1, HAL_PIXEL_FORMAT_RGBA_8888},
+ { 100, 100, 3, HAL_PIXEL_FORMAT_RGBA_8888},
};
+#if CPU_CONSUMER_TEST_FORMAT_Y8
+INSTANTIATE_TEST_CASE_P(Y8Tests,
+ CpuConsumerTest,
+ ::testing::ValuesIn(y8TestSets));
+#endif
+
+#if CPU_CONSUMER_TEST_FORMAT_Y16
+INSTANTIATE_TEST_CASE_P(Y16Tests,
+ CpuConsumerTest,
+ ::testing::ValuesIn(y16TestSets));
+#endif
+
+#if CPU_CONSUMER_TEST_FORMAT_RAW
INSTANTIATE_TEST_CASE_P(RawTests,
CpuConsumerTest,
::testing::ValuesIn(rawTestSets));
+#endif
+
+#if CPU_CONSUMER_TEST_FORMAT_RGBA_8888
+INSTANTIATE_TEST_CASE_P(Rgba8888Tests,
+ CpuConsumerTest,
+ ::testing::ValuesIn(rgba8888TestSets));
+#endif
+
+
} // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index ec14a0d..7376b4c 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -19,7 +19,7 @@
#include <EGL/egl.h>
#include <gtest/gtest.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
#include <system/graphics.h>
#include <utils/Log.h>
#include <utils/Thread.h>
@@ -40,8 +40,8 @@ protected:
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
- mST = new SurfaceTexture(123);
- mSTC = new SurfaceTextureClient(mST);
+ mST = new GLConsumer(123);
+ mSTC = new Surface(mST->getBufferQueue());
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
@@ -61,6 +61,7 @@ protected:
&myConfig, 1, &numConfigs));
ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ mEglConfig = myConfig;
EGLint pbufferAttribs[] = {
EGL_WIDTH, 16,
EGL_HEIGHT, 16,
@@ -95,24 +96,25 @@ protected:
virtual EGLint const* getConfigAttribs() {
static EGLint sDefaultConfigAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | EGL_WINDOW_BIT,
EGL_NONE
};
return sDefaultConfigAttribs;
}
- sp<SurfaceTexture> mST;
- sp<SurfaceTextureClient> mSTC;
+ sp<GLConsumer> mST;
+ sp<Surface> mSTC;
sp<ANativeWindow> mANW;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
EGLContext mEglContext;
+ EGLConfig mEglConfig;
};
TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
- sp<ISurfaceTexture> ist(mSTC->getISurfaceTexture());
+ sp<IGraphicBufferProducer> ist(mSTC->getIGraphicBufferProducer());
ASSERT_TRUE(ist != NULL);
}
@@ -128,7 +130,7 @@ TEST_F(SurfaceTextureClientTest, ConcreteTypeIsSurfaceTextureClient) {
int result = -123;
int err = mANW->query(mANW.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result);
EXPECT_EQ(NO_ERROR, err);
- EXPECT_EQ(NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT, result);
+ EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
}
TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) {
@@ -169,6 +171,34 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) {
eglTerminate(dpy);
}
+TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) {
+
+ EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL);
+ EXPECT_NE(EGL_NO_SURFACE, eglSurface);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLBoolean success = eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext);
+ EXPECT_TRUE(success);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ success = eglSwapBuffers(mEglDisplay, eglSurface);
+ EXPECT_TRUE(success);
+
+ mST->abandon();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ success = eglSwapBuffers(mEglDisplay, eglSurface);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(EGL_BAD_SURFACE, eglGetError());
+
+ success = eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
+ ASSERT_TRUE(success);
+
+ if (eglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, eglSurface);
+ }
+}
+
TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) {
EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), -1, 0, 0));
EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), 0, -1, 0));
@@ -250,7 +280,7 @@ TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
}
TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) {
- sp<SurfaceTexture> st(mST);
+ sp<GLConsumer> st(mST);
ANativeWindowBuffer* buf;
EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
@@ -464,7 +494,7 @@ TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) {
// from the SurfaceTexture class.
TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
class MyThread : public Thread {
- sp<SurfaceTexture> mST;
+ sp<GLConsumer> mST;
EGLContext ctx;
EGLSurface sur;
EGLDisplay dpy;
@@ -480,7 +510,7 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
return false;
}
public:
- MyThread(const sp<SurfaceTexture>& mST)
+ MyThread(const sp<GLConsumer>& mST)
: mST(mST), mBufferRetired(false) {
ctx = eglGetCurrentContext();
sur = eglGetCurrentSurface(EGL_DRAW);
@@ -685,8 +715,8 @@ protected:
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<SurfaceTexture> st(new SurfaceTexture(i));
- sp<SurfaceTextureClient> stc(new SurfaceTextureClient(st));
+ sp<GLConsumer> st(new GLConsumer(i));
+ sp<Surface> stc(new Surface(st->getBufferQueue()));
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), NULL);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index d9b40cf..dd6c435 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -18,8 +18,7 @@
//#define LOG_NDEBUG 0
#include <gtest/gtest.h>
-#include <gui/SurfaceTexture.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/GLConsumer.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
#include <utils/threads.h>
@@ -30,10 +29,14 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <ui/FramebufferNativeWindow.h>
+#include <utils/UniquePtr.h>
+#include <android/native_window.h>
namespace android {
@@ -202,7 +205,6 @@ protected:
while ((err = glGetError()) != GL_NO_ERROR) {
msg += String8::format(", %#x", err);
}
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
return ::testing::AssertionFailure(
::testing::Message(msg.string()));
}
@@ -228,7 +230,6 @@ protected:
msg += String8::format("a(%d isn't %d)", pixel[3], a);
}
if (!msg.isEmpty()) {
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
return ::testing::AssertionFailure(
::testing::Message(msg.string()));
} else {
@@ -378,14 +379,108 @@ static int abs(int value) {
// XXX: Code above this point should live elsewhere
+class MultiTextureConsumerTest : public GLTest {
+protected:
+ enum { TEX_ID = 123 };
+
+ virtual void SetUp() {
+ GLTest::SetUp();
+ mGlConsumer = new GLConsumer(TEX_ID);
+ mSurface = new Surface(mGlConsumer->getBufferQueue());
+ mANW = mSurface.get();
+
+ }
+ virtual void TearDown() {
+ GLTest::TearDown();
+ }
+ virtual EGLint const* getContextAttribs() {
+ return NULL;
+ }
+ virtual EGLint const* getConfigAttribs() {
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE };
+
+ return sDefaultConfigAttribs;
+ }
+ sp<GLConsumer> mGlConsumer;
+ sp<Surface> mSurface;
+ ANativeWindow* mANW;
+};
+
+
+TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) {
+ ANativeWindow_Buffer buffer;
+
+ ASSERT_EQ(native_window_set_usage(mANW, GRALLOC_USAGE_SW_WRITE_OFTEN), NO_ERROR);
+ ASSERT_EQ(native_window_set_buffers_format(mANW, HAL_PIXEL_FORMAT_RGBA_8888), NO_ERROR);
+
+ glShadeModel(GL_FLAT);
+ glDisable(GL_DITHER);
+ glDisable(GL_CULL_FACE);
+ glViewport(0, 0, getSurfaceWidth(), getSurfaceHeight());
+ glOrthof(0, getSurfaceWidth(), 0, getSurfaceHeight(), 0, 1);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glColor4f(1, 1, 1, 1);
+
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID);
+ glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ uint32_t texel = 0x80808080;
+ glBindTexture(GL_TEXTURE_2D, TEX_ID+1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texel);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, TEX_ID+1);
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID);
+ glEnable(GL_TEXTURE_EXTERNAL_OES);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (int i=0 ; i<8 ; i++) {
+ mSurface->lock(&buffer, NULL);
+ memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4);
+ mSurface->unlockAndPost();
+
+ mGlConsumer->updateTexImage();
+
+ GLfloat vertices[][2] = { {i*16.0f, 0}, {(i+1)*16.0f, 0}, {(i+1)*16.0f, 16.0f}, {i*16.0f, 16.0f} };
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ }
+
+ for (int i=0 ; i<8 ; i++) {
+ EXPECT_TRUE(checkPixel(i*16 + 8, 8, i*16, i*16, i*16, i*16, 0));
+ }
+}
+
+
+
class SurfaceTextureGLTest : public GLTest {
protected:
enum { TEX_ID = 123 };
virtual void SetUp() {
GLTest::SetUp();
- mST = new SurfaceTexture(TEX_ID);
- mSTC = new SurfaceTextureClient(mST);
+ mST = new GLConsumer(TEX_ID);
+ mSTC = new Surface(mST->getBufferQueue());
mANW = mSTC;
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp());
@@ -406,7 +501,7 @@ protected:
class TextureRenderer: public RefBase {
public:
- TextureRenderer(GLuint texName, const sp<SurfaceTexture>& st):
+ TextureRenderer(GLuint texName, const sp<GLConsumer>& st):
mTexName(texName),
mST(st) {
}
@@ -447,7 +542,7 @@ protected:
ASSERT_NE(-1, mTexMatrixHandle);
}
- // drawTexture draws the SurfaceTexture over the entire GL viewport.
+ // drawTexture draws the GLConsumer over the entire GL viewport.
void drawTexture() {
static const GLfloat triangleVertices[] = {
-1.0f, 1.0f,
@@ -494,14 +589,14 @@ protected:
}
GLuint mTexName;
- sp<SurfaceTexture> mST;
+ sp<GLConsumer> mST;
GLuint mPgm;
GLint mPositionHandle;
GLint mTexSamplerHandle;
GLint mTexMatrixHandle;
};
- class FrameWaiter : public SurfaceTexture::FrameAvailableListener {
+ class FrameWaiter : public GLConsumer::FrameAvailableListener {
public:
FrameWaiter():
mPendingFrames(0) {
@@ -526,7 +621,7 @@ protected:
Condition mCondition;
};
- // Note that SurfaceTexture will lose the notifications
+ // Note that GLConsumer will lose the notifications
// onBuffersReleased and onFrameAvailable as there is currently
// no way to forward the events. This DisconnectWaiter will not let the
// disconnect finish until finishDisconnect() is called. It will
@@ -575,8 +670,8 @@ protected:
Condition mFrameCondition;
};
- sp<SurfaceTexture> mST;
- sp<SurfaceTextureClient> mSTC;
+ sp<GLConsumer> mST;
+ sp<Surface> mSTC;
sp<ANativeWindow> mANW;
sp<TextureRenderer> mTextureRenderer;
sp<FrameWaiter> mFW;
@@ -719,18 +814,18 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
glViewport(0, 0, texWidth, texHeight);
drawTexture();
- EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255));
- EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255));
- EXPECT_TRUE(checkPixel(63, 65, 0, 133, 0, 255));
- EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255));
-
- EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255));
- EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255));
- EXPECT_TRUE(checkPixel(52, 51, 98, 255, 73, 255));
- EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255));
- EXPECT_TRUE(checkPixel(31, 9, 107, 24, 87, 255));
- EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255));
- EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255));
+ EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255, 3));
+ EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255, 3));
+ EXPECT_TRUE(checkPixel(63, 65, 0, 133, 0, 255, 3));
+ EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255, 3));
+
+ EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255, 3));
+ EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255, 3));
+ EXPECT_TRUE(checkPixel(52, 51, 98, 255, 73, 255, 3));
+ EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255, 3));
+ EXPECT_TRUE(checkPixel(31, 9, 107, 24, 87, 255, 3));
+ EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255, 3));
+ EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255, 3));
}
TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) {
@@ -1070,7 +1165,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) {
EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35));
}
-// Tests if SurfaceTexture and BufferQueue are robust enough
+// Tests if GLConsumer and BufferQueue are robust enough
// to handle a special case where updateTexImage is called
// in the middle of disconnect. This ordering is enforced
// by blocking in the disconnect callback.
@@ -1123,12 +1218,12 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) {
sp<Thread> pt(new ProducerThread(mANW));
pt->run();
- // eat a frame so SurfaceTexture will own an at least one slot
+ // eat a frame so GLConsumer will own an at least one slot
dw->waitForFrame();
EXPECT_EQ(OK,mST->updateTexImage());
dw->waitForFrame();
- // Could fail here as SurfaceTexture thinks it still owns the slot
+ // Could fail here as GLConsumer thinks it still owns the slot
// but bufferQueue has released all slots
EXPECT_EQ(OK,mST->updateTexImage());
@@ -1136,7 +1231,7 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) {
}
-// This test ensures that the SurfaceTexture clears the mCurrentTexture
+// This test ensures that the GLConsumer clears the mCurrentTexture
// when it is disconnected and reconnected. Otherwise it will
// attempt to release a buffer that it does not owned
TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) {
@@ -1581,7 +1676,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) {
// This test should have the only reference to buffer 0.
EXPECT_EQ(1, buffers[0]->getStrongCount());
- // The SurfaceTexture should hold a single reference to buffer 1 in its
+ // The GLConsumer should hold a single reference to buffer 1 in its
// mCurrentBuffer member. All of the references in the slots should have
// been released.
EXPECT_EQ(2, buffers[1]->getStrongCount());
@@ -1615,7 +1710,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
buffers[i] = mST->getCurrentBuffer();
}
- // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
+ // Abandon the GLConsumer, releasing the ref that the GLConsumer has
// on buffers[2].
mST->abandon();
@@ -1639,6 +1734,81 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
}
}
+TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentBeforeConsumerDeathUnrefsBuffers) {
+ sp<GraphicBuffer> buffer;
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+
+ // Produce a frame
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // Destroy the EGLSurface.
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ mProducerEglSurface = EGL_NO_SURFACE;
+ mSTC.clear();
+ mANW.clear();
+ mTextureRenderer.clear();
+
+ // Consume a frame
+ ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+ buffer = mST->getCurrentBuffer();
+
+ // Destroy the GL texture object to release its ref
+ GLuint texID = TEX_ID;
+ glDeleteTextures(1, &texID);
+
+ // make un-current, all references to buffer should be gone
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT));
+
+ // Destroy consumer
+ mST.clear();
+
+ EXPECT_EQ(1, buffer->getStrongCount());
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentAfterConsumerDeathUnrefsBuffers) {
+ sp<GraphicBuffer> buffer;
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+
+ // Produce a frame
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // Destroy the EGLSurface.
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ mProducerEglSurface = EGL_NO_SURFACE;
+ mSTC.clear();
+ mANW.clear();
+ mTextureRenderer.clear();
+
+ // Consume a frame
+ ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+ buffer = mST->getCurrentBuffer();
+
+ // Destroy the GL texture object to release its ref
+ GLuint texID = TEX_ID;
+ glDeleteTextures(1, &texID);
+
+ // Destroy consumer
+ mST.clear();
+
+ // make un-current, all references to buffer should be gone
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT));
+
+ EXPECT_EQ(1, buffer->getStrongCount());
+}
+
+
TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) {
// This test requires 3 buffers to run on a single thread.
mST->setDefaultMaxBufferCount(3);
@@ -1847,7 +2017,7 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) {
* This test fixture is for testing GL -> GL texture streaming from one thread
* to another. It contains functionality to create a producer thread that will
* perform GL rendering to an ANativeWindow that feeds frames to a
- * SurfaceTexture. Additionally it supports interlocking the producer and
+ * GLConsumer. Additionally it supports interlocking the producer and
* consumer threads so that a specific sequence of calls can be
* deterministically created by the test.
*
@@ -1914,13 +2084,13 @@ protected:
// FrameCondition is a utility class for interlocking between the producer
// and consumer threads. The FrameCondition object should be created and
// destroyed in the consumer thread only. The consumer thread should set
- // the FrameCondition as the FrameAvailableListener of the SurfaceTexture,
+ // the FrameCondition as the FrameAvailableListener of the GLConsumer,
// and should call both waitForFrame and finishFrame once for each expected
// frame.
//
// This interlocking relies on the fact that onFrameAvailable gets called
- // synchronously from SurfaceTexture::queueBuffer.
- class FrameCondition : public SurfaceTexture::FrameAvailableListener {
+ // synchronously from GLConsumer::queueBuffer.
+ class FrameCondition : public GLConsumer::FrameAvailableListener {
public:
FrameCondition():
mFrameAvailable(false),
@@ -1951,7 +2121,7 @@ protected:
ALOGV("-finishFrame");
}
- // This should be called by SurfaceTexture on the producer thread.
+ // This should be called by GLConsumer on the producer thread.
virtual void onFrameAvailable() {
Mutex::Autolock lock(mMutex);
ALOGV("+onFrameAvailable");
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 545b547..429becf 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -23,11 +23,17 @@
#include <utils/String8.h>
#include <private/gui/ComposerService.h>
+#include <binder/ProcessState.h>
namespace android {
class SurfaceTest : public ::testing::Test {
protected:
+
+ SurfaceTest() {
+ ProcessState::self()->startThreadPool();
+ }
+
virtual void SetUp() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
@@ -81,14 +87,11 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) {
sp<ANativeWindow> anw(mSurface);
// Verify the screenshot works with no protected buffers.
- sp<IMemoryHeap> heap;
- uint32_t w=0, h=0;
- PixelFormat fmt=0;
+ sp<CpuConsumer> consumer = new CpuConsumer(1);
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &heap, &w, &h, &fmt, 64, 64, 0,
- 0x7fffffff));
- ASSERT_TRUE(heap != NULL);
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(),
+ 64, 64, 0, 0x7fffffff, true));
// Set the PROTECTED usage bit and verify that the screenshot fails. Note
// that we need to dequeue a buffer in order for it to actually get
@@ -116,11 +119,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) {
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- heap = 0;
- w = h = fmt = 0;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &heap, &w, &h, &fmt,
- 64, 64, 0, 0x7fffffff));
- ASSERT_TRUE(heap != NULL);
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(),
+ 64, 64, 0, 0x7fffffff, true));
}
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 0d2e44c..008446b 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -30,7 +30,8 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware \
libsync \
- libutils
+ libutils \
+ liblog
ifneq ($(BOARD_FRAMEBUFFER_FORCE_FORMAT),)
LOCAL_CFLAGS += -DFRAMEBUFFER_FORCE_FORMAT=$(BOARD_FRAMEBUFFER_FORCE_FORMAT)
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index d214b97..464ee86 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -18,6 +18,9 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+ // This is needed for stdint.h to define INT64_MAX in C++
+ #define __STDC_LIMIT_MACROS
+
#include <sync/sync.h>
#include <ui/Fence.h>
#include <unistd.h>
@@ -26,7 +29,7 @@
namespace android {
-const sp<Fence> Fence::NO_FENCE = sp<Fence>();
+const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence);
Fence::Fence() :
mFenceFd(-1) {
@@ -51,11 +54,12 @@ status_t Fence::wait(unsigned int timeout) {
return err < 0 ? -errno : status_t(NO_ERROR);
}
-status_t Fence::waitForever(unsigned int warningTimeout, const char* logname) {
+status_t Fence::waitForever(const char* logname) {
ATRACE_CALL();
if (mFenceFd == -1) {
return NO_ERROR;
}
+ unsigned int warningTimeout = 3000;
int err = sync_wait(mFenceFd, warningTimeout);
if (err < 0 && errno == ETIME) {
ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd,
@@ -68,7 +72,19 @@ status_t Fence::waitForever(unsigned int warningTimeout, const char* logname) {
sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
const sp<Fence>& f2) {
ATRACE_CALL();
- int result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd);
+ int result;
+ // Merge the two fences. In the case where one of the fences is not a
+ // valid fence (e.g. NO_FENCE) we merge the one valid fence with itself so
+ // that a new fence with the given name is created.
+ if (f1->isValid() && f2->isValid()) {
+ result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd);
+ } else if (f1->isValid()) {
+ result = sync_merge(name.string(), f1->mFenceFd, f1->mFenceFd);
+ } else if (f2->isValid()) {
+ result = sync_merge(name.string(), f2->mFenceFd, f2->mFenceFd);
+ } else {
+ return NO_FENCE;
+ }
if (result == -1) {
status_t err = -errno;
ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)",
@@ -80,10 +96,34 @@ sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
}
int Fence::dup() const {
+ return ::dup(mFenceFd);
+}
+
+nsecs_t Fence::getSignalTime() const {
if (mFenceFd == -1) {
return -1;
}
- return ::dup(mFenceFd);
+
+ struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
+ if (finfo == NULL) {
+ ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
+ return -1;
+ }
+ if (finfo->status != 1) {
+ sync_fence_info_free(finfo);
+ return INT64_MAX;
+ }
+
+ struct sync_pt_info* pinfo = NULL;
+ uint64_t timestamp = 0;
+ while ((pinfo = sync_pt_info(finfo, pinfo)) != NULL) {
+ if (pinfo->timestamp_ns > timestamp) {
+ timestamp = pinfo->timestamp_ns;
+ }
+ }
+ sync_fence_info_free(finfo);
+
+ return nsecs_t(timestamp);
}
size_t Fence::getFlattenedSize() const {
@@ -91,22 +131,24 @@ size_t Fence::getFlattenedSize() const {
}
size_t Fence::getFdCount() const {
- return 1;
+ return isValid() ? 1 : 0;
}
status_t Fence::flatten(void* buffer, size_t size, int fds[],
size_t count) const {
- if (size != 0 || count != 1) {
+ if (size != getFlattenedSize() || count != getFdCount()) {
return BAD_VALUE;
}
- fds[0] = mFenceFd;
+ if (isValid()) {
+ fds[0] = mFenceFd;
+ }
return NO_ERROR;
}
status_t Fence::unflatten(void const* buffer, size_t size, int fds[],
size_t count) {
- if (size != 0 || count != 1) {
+ if (size != 0 || (count != 0 && count != 1)) {
return BAD_VALUE;
}
if (mFenceFd != -1) {
@@ -114,7 +156,10 @@ status_t Fence::unflatten(void const* buffer, size_t size, int fds[],
return INVALID_OPERATION;
}
- mFenceFd = fds[0];
+ if (count == 1) {
+ mFenceFd = fds[0];
+ }
+
return NO_ERROR;
}
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index b9cab85..580788d 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -174,6 +174,27 @@ status_t GraphicBuffer::lock(uint32_t usage, const Rect& rect, void** vaddr)
return res;
}
+status_t GraphicBuffer::lockYCbCr(uint32_t usage, android_ycbcr *ycbcr)
+{
+ const Rect lockBounds(width, height);
+ status_t res = lockYCbCr(usage, lockBounds, ycbcr);
+ return res;
+}
+
+status_t GraphicBuffer::lockYCbCr(uint32_t usage, const Rect& rect,
+ android_ycbcr *ycbcr)
+{
+ if (rect.left < 0 || rect.right > this->width ||
+ rect.top < 0 || rect.bottom > this->height) {
+ ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
+ rect.left, rect.top, rect.right, rect.bottom,
+ this->width, this->height);
+ return BAD_VALUE;
+ }
+ status_t res = getBufferMapper().lockYCbCr(handle, usage, rect, ycbcr);
+ return res;
+}
+
status_t GraphicBuffer::unlock()
{
status_t res = getBufferMapper().unlock(handle);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index fb43410..ff550d9 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -90,105 +90,6 @@ void GraphicBufferAllocator::dumpToSystemLog()
ALOGD("%s", s.string());
}
-class BufferLiberatorThread : public Thread {
-public:
-
- static void queueCaptiveBuffer(buffer_handle_t handle) {
- size_t queueSize;
- {
- Mutex::Autolock lock(sMutex);
- if (sThread == NULL) {
- sThread = new BufferLiberatorThread;
- sThread->run("BufferLiberator");
- }
-
- sThread->mQueue.push_back(handle);
- sThread->mQueuedCondition.signal();
- queueSize = sThread->mQueue.size();
- }
- }
-
- static void waitForLiberation() {
- Mutex::Autolock lock(sMutex);
-
- waitForLiberationLocked();
- }
-
- static void maybeWaitForLiberation() {
- Mutex::Autolock lock(sMutex);
- if (sThread != NULL) {
- if (sThread->mQueue.size() > 8) {
- waitForLiberationLocked();
- }
- }
- }
-
-private:
-
- BufferLiberatorThread() {}
-
- virtual bool threadLoop() {
- buffer_handle_t handle;
- { // Scope for mutex
- Mutex::Autolock lock(sMutex);
- while (mQueue.isEmpty()) {
- mQueuedCondition.wait(sMutex);
- }
- handle = mQueue[0];
- }
-
- status_t err;
- GraphicBufferAllocator& gba(GraphicBufferAllocator::get());
- { // Scope for tracing
- ATRACE_NAME("gralloc::free");
- err = gba.mAllocDev->free(gba.mAllocDev, handle);
- }
- ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err));
-
- if (err == NO_ERROR) {
- Mutex::Autolock _l(GraphicBufferAllocator::sLock);
- KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t>&
- list(GraphicBufferAllocator::sAllocList);
- list.removeItem(handle);
- }
-
- { // Scope for mutex
- Mutex::Autolock lock(sMutex);
- mQueue.removeAt(0);
- mFreedCondition.broadcast();
- }
-
- return true;
- }
-
- static void waitForLiberationLocked() {
- if (sThread == NULL) {
- return;
- }
-
- const nsecs_t timeout = 500 * 1000 * 1000;
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t timeToStop = now + timeout;
- while (!sThread->mQueue.isEmpty() && now < timeToStop) {
- sThread->mFreedCondition.waitRelative(sMutex, timeToStop - now);
- now = systemTime(SYSTEM_TIME_MONOTONIC);
- }
-
- if (!sThread->mQueue.isEmpty()) {
- ALOGW("waitForLiberationLocked timed out");
- }
- }
-
- static Mutex sMutex;
- static sp<BufferLiberatorThread> sThread;
- Vector<buffer_handle_t> mQueue;
- Condition mQueuedCondition;
- Condition mFreedCondition;
-};
-
-Mutex BufferLiberatorThread::sMutex;
-sp<BufferLiberatorThread> BufferLiberatorThread::sThread;
-
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
@@ -199,24 +100,13 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma
w = h = 1;
// we have a h/w allocator and h/w buffer is requested
- status_t err;
-
- // If too many async frees are queued up then wait for some of them to
- // complete before attempting to allocate more memory. This is exercised
- // by the android.opengl.cts.GLSurfaceViewTest CTS test.
- BufferLiberatorThread::maybeWaitForLiberation();
-
+ status_t err;
+
err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
- if (err != NO_ERROR) {
- ALOGW("WOW! gralloc alloc failed, waiting for pending frees!");
- BufferLiberatorThread::waitForLiberation();
- err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
- }
-
ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)",
w, h, format, usage, err, strerror(-err));
-
+
if (err == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
@@ -239,11 +129,21 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma
return err;
}
-
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
- BufferLiberatorThread::queueCaptiveBuffer(handle);
- return NO_ERROR;
+ ATRACE_CALL();
+ status_t err;
+
+ err = mAllocDev->free(mAllocDev, handle);
+
+ ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err));
+ if (err == NO_ERROR) {
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ list.removeItem(handle);
+ }
+
+ return err;
}
// ---------------------------------------------------------------------------
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 967da98..a4cfce2 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -84,6 +84,20 @@ status_t GraphicBufferMapper::lock(buffer_handle_t handle,
return err;
}
+status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle,
+ int usage, const Rect& bounds, android_ycbcr *ycbcr)
+{
+ ATRACE_CALL();
+ status_t err;
+
+ err = mAllocMod->lock_ycbcr(mAllocMod, handle, usage,
+ bounds.left, bounds.top, bounds.width(), bounds.height(),
+ ycbcr);
+
+ ALOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err));
+ return err;
+}
+
status_t GraphicBufferMapper::unlock(buffer_handle_t handle)
{
ATRACE_CALL();
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 932ef68..bf01488 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -47,6 +47,11 @@ enum {
op_xor = region_operator<Rect>::op_xor
};
+enum {
+ direction_LTR,
+ direction_RTL
+};
+
// ----------------------------------------------------------------------------
Region::Region() {
@@ -69,6 +74,133 @@ Region::~Region()
{
}
+/**
+ * Copy rects from the src vector into the dst vector, resolving vertical T-Junctions along the way
+ *
+ * First pass through, divideSpanRTL will be set because the 'previous span' (indexing into the dst
+ * vector) will be reversed. Each rectangle in the original list, starting from the bottom, will be
+ * compared with the span directly below, and subdivided as needed to resolve T-junctions.
+ *
+ * The resulting temporary vector will be a completely reversed copy of the original, without any
+ * bottom-up T-junctions.
+ *
+ * Second pass through, divideSpanRTL will be false since the previous span will index into the
+ * final, correctly ordered region buffer. Each rectangle will be compared with the span directly
+ * above it, and subdivided to resolve any remaining T-junctions.
+ */
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
+ Vector<Rect>& dst, int spanDirection) {
+ dst.clear();
+
+ const Rect* current = end - 1;
+ int lastTop = current->top;
+
+ // add first span immediately
+ do {
+ dst.add(*current);
+ current--;
+ } while (current->top == lastTop && current >= begin);
+
+ unsigned int beginLastSpan = -1;
+ unsigned int endLastSpan = -1;
+ int top = -1;
+ int bottom = -1;
+
+ // for all other spans, split if a t-junction exists in the span directly above
+ while (current >= begin) {
+ if (current->top != (current + 1)->top) {
+ // new span
+ if ((spanDirection == direction_RTL && current->bottom != (current + 1)->top) ||
+ (spanDirection == direction_LTR && current->top != (current + 1)->bottom)) {
+ // previous span not directly adjacent, don't check for T junctions
+ beginLastSpan = INT_MAX;
+ } else {
+ beginLastSpan = endLastSpan + 1;
+ }
+ endLastSpan = dst.size() - 1;
+
+ top = current->top;
+ bottom = current->bottom;
+ }
+ int left = current->left;
+ int right = current->right;
+
+ for (unsigned int prevIndex = beginLastSpan; prevIndex <= endLastSpan; prevIndex++) {
+ const Rect* prev = &dst[prevIndex];
+ if (spanDirection == direction_RTL) {
+ // iterating over previous span RTL, quit if it's too far left
+ if (prev->right <= left) break;
+
+ if (prev->right > left && prev->right < right) {
+ dst.add(Rect(prev->right, top, right, bottom));
+ right = prev->right;
+ }
+
+ if (prev->left > left && prev->left < right) {
+ dst.add(Rect(prev->left, top, right, bottom));
+ right = prev->left;
+ }
+
+ // if an entry in the previous span is too far right, nothing further left in the
+ // current span will need it
+ if (prev->left >= right) {
+ beginLastSpan = prevIndex;
+ }
+ } else {
+ // iterating over previous span LTR, quit if it's too far right
+ if (prev->left >= right) break;
+
+ if (prev->left > left && prev->left < right) {
+ dst.add(Rect(left, top, prev->left, bottom));
+ left = prev->left;
+ }
+
+ if (prev->right > left && prev->right < right) {
+ dst.add(Rect(left, top, prev->right, bottom));
+ left = prev->right;
+ }
+ // if an entry in the previous span is too far left, nothing further right in the
+ // current span will need it
+ if (prev->right <= left) {
+ beginLastSpan = prevIndex;
+ }
+ }
+ }
+
+ if (left < right) {
+ dst.add(Rect(left, top, right, bottom));
+ }
+
+ current--;
+ }
+}
+
+/**
+ * Creates a new region with the same data as the argument, but divides rectangles as necessary to
+ * remove T-Junctions
+ *
+ * Note: the output will not necessarily be a very efficient representation of the region, since it
+ * may be that a triangle-based approach would generate significantly simpler geometry
+ */
+Region Region::createTJunctionFreeRegion(const Region& r) {
+ if (r.isEmpty()) return r;
+ if (r.isRect()) return r;
+
+ Vector<Rect> reversed;
+ reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
+
+ Region outputRegion;
+ reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
+ outputRegion.mStorage, direction_LTR);
+ outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
+
+#if VALIDATE_REGIONS
+ validate(outputRegion, "T-Junction free region");
+#endif
+
+ return outputRegion;
+}
+
Region& Region::operator = (const Region& rhs)
{
#if VALIDATE_REGIONS
@@ -107,6 +239,10 @@ void Region::set(uint32_t w, uint32_t h)
mStorage.add(Rect(w,h));
}
+bool Region::isTriviallyEqual(const Region& region) const {
+ return begin() == region.begin();
+}
+
// ----------------------------------------------------------------------------
void Region::addRectUnchecked(int l, int t, int r, int b)
@@ -398,9 +534,7 @@ bool Region::validate(const Region& reg, const char* name, bool silent)
}
if (result == false && !silent) {
reg.dump(name);
- CallStack stack;
- stack.update();
- stack.dump("");
+ CallStack stack(LOG_TAG);
}
return result;
}
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 50cad36..8b8e1d8 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -1,4 +1,28 @@
# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+ Region_test.cpp
+
+shared_libraries := \
+ libui
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval include $(BUILD_NATIVE_TEST)) \
+)
+
+# Build the unit tests.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
new file mode 100644
index 0000000..b104a46
--- /dev/null
+++ b/libs/ui/tests/Region_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 LOG_TAG "RegionTest"
+
+#include <stdlib.h>
+#include <ui/Region.h>
+#include <ui/Rect.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class RegionTest : public testing::Test {
+protected:
+ void checkVertTJunction(const Rect* lhs, const Rect* rhs) {
+ EXPECT_FALSE((rhs->right > lhs->left && rhs->right < lhs->right) ||
+ (rhs->left > lhs->left && rhs->left < lhs->right));
+ }
+
+ void verifyNoTJunctions(const Region& r) {
+ for (const Rect* current = r.begin(); current < r.end(); current++) {
+ for (const Rect* other = current - 1; other >= r.begin(); other--) {
+ if (other->bottom < current->top) break;
+ if (other->bottom != current->top) continue;
+ checkVertTJunction(current, other);
+ }
+ for (const Rect* other = current + 1; other < r.end(); other++) {
+ if (other->top > current->bottom) break;
+ if (other->top != current->bottom) continue;
+ checkVertTJunction(current, other);
+ }
+ }
+ }
+
+ void checkTJunctionFreeFromRegion(const Region& original, int expectedCount = -1) {
+ Region modified = Region::createTJunctionFreeRegion(original);
+ verifyNoTJunctions(modified);
+ if (expectedCount != -1) {
+ EXPECT_EQ(modified.end() - modified.begin(), expectedCount);
+ }
+ EXPECT_TRUE((original ^ modified).isEmpty());
+ }
+};
+
+TEST_F(RegionTest, MinimalDivision_TJunction) {
+ Region r;
+ // | x |
+ // |xxx|
+ r.clear();
+ r.orSelf(Rect(1, 0, 2, 1));
+ r.orSelf(Rect(0, 1, 3, 2));
+ checkTJunctionFreeFromRegion(r, 4);
+
+ // | x |
+ // | |
+ // |xxx|
+ r.clear();
+ r.orSelf(Rect(1, 0, 2, 1));
+ r.orSelf(Rect(0, 2, 3, 3));
+ checkTJunctionFreeFromRegion(r, 2);
+}
+
+TEST_F(RegionTest, Trivial_TJunction) {
+ Region r;
+ checkTJunctionFreeFromRegion(r);
+
+ r.orSelf(Rect(100, 100, 500, 500));
+ checkTJunctionFreeFromRegion(r);
+}
+
+TEST_F(RegionTest, Simple_TJunction) {
+ Region r;
+ // | x |
+ // |xxxx|
+ // |xxxx|
+ // |xxxx|
+ r.clear();
+ r.orSelf(Rect(1, 0, 2, 1));
+ r.orSelf(Rect(0, 1, 3, 3));
+ checkTJunctionFreeFromRegion(r);
+
+ // | x |
+ // |xx |
+ // |xxx|
+ r.clear();
+ r.orSelf(Rect(2,0,4,2));
+ r.orSelf(Rect(0,2,4,4));
+ r.orSelf(Rect(0,4,6,6));
+ checkTJunctionFreeFromRegion(r);
+
+ // |x x|
+ // |xxx|
+ // |x x|
+ r.clear();
+ r.orSelf(Rect(0,0,2,6));
+ r.orSelf(Rect(4,0,6,6));
+ r.orSelf(Rect(0,2,6,4));
+ checkTJunctionFreeFromRegion(r);
+
+ // |xxx|
+ // | x |
+ // | x |
+ r.clear();
+ r.orSelf(Rect(0,0,6,2));
+ r.orSelf(Rect(2,2,4,6));
+ checkTJunctionFreeFromRegion(r);
+}
+
+TEST_F(RegionTest, Bigger_TJunction) {
+ Region r;
+ // |xxxx |
+ // | xxxx |
+ // | xxxx |
+ // | xxxx|
+ for (int i = 0; i < 4; i++) {
+ r.orSelf(Rect(i,i,i+4,i+1));
+ }
+ checkTJunctionFreeFromRegion(r, 16);
+}
+
+#define ITER_MAX 1000
+#define X_MAX 8
+#define Y_MAX 8
+
+TEST_F(RegionTest, Random_TJunction) {
+ Region r;
+ srandom(12345);
+
+ for (int iter = 0; iter < ITER_MAX; iter++) {
+ r.clear();
+ for (int i = 0; i < X_MAX; i++) {
+ for (int j = 0; j < Y_MAX; j++) {
+ if (random() % 2) {
+ r.orSelf(Rect(i, j, i + 1, j + 1));
+ }
+ }
+ }
+ checkTJunctionFreeFromRegion(r);
+ }
+}
+
+}; // namespace android
+
diff --git a/libs/ui/tests/region/Android.mk b/libs/ui/tests/region/Android.mk
deleted file mode 100644
index 6cc4a5a..0000000
--- a/libs/ui/tests/region/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- region.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libui
-
-LOCAL_MODULE:= test-region
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_EXECUTABLE)
diff --git a/libs/ui/tests/region/region.cpp b/libs/ui/tests/region/region.cpp
deleted file mode 100644
index 6347294..0000000
--- a/libs/ui/tests/region/region.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2009 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 LOG_TAG "Region"
-
-#include <stdio.h>
-#include <utils/Debug.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-using namespace android;
-
-int main()
-{
- Region empty;
- Region reg0( Rect( 0, 0, 100, 100 ) );
- Region reg1 = reg0;
- Region reg2, reg3;
-
- Region reg4 = empty | reg1;
- Region reg5 = reg1 | empty;
-
- reg4.dump("reg4");
- reg5.dump("reg5");
-
- reg0.dump("reg0");
- reg1.dump("reg1");
-
- reg0 = reg0 | reg0.translate(150, 0);
- reg0.dump("reg0");
- reg1.dump("reg1");
-
- reg0 = reg0 | reg0.translate(300, 0);
- reg0.dump("reg0");
- reg1.dump("reg1");
-
- //reg2 = reg0 | reg0.translate(0, 100);
- //reg0.dump("reg0");
- //reg1.dump("reg1");
- //reg2.dump("reg2");
-
- //reg3 = reg0 | reg0.translate(0, 150);
- //reg0.dump("reg0");
- //reg1.dump("reg1");
- //reg2.dump("reg2");
- //reg3.dump("reg3");
-
- ALOGD("---");
- reg2 = reg0 | reg0.translate(100, 0);
- reg0.dump("reg0");
- reg1.dump("reg1");
- reg2.dump("reg2");
-
- return 0;
-}
-
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index c9f8fd4..cbfe7bd 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -25,6 +25,8 @@ commonSources:= \
Debug.cpp \
FileMap.cpp \
Flattenable.cpp \
+ JenkinsHash.cpp \
+ LinearAllocator.cpp \
LinearTransform.cpp \
Log.cpp \
PropertyMap.cpp \
@@ -111,6 +113,10 @@ ifeq ($(TARGET_OS),linux)
LOCAL_LDLIBS += -lrt -ldl
endif
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_CFLAGS += -DALIGN_DOUBLE
+endif
+
LOCAL_C_INCLUDES += \
bionic/libc/private \
external/zlib
diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp
index fb8ec9f..fd51b7b 100644
--- a/libs/utils/BasicHashtable.cpp
+++ b/libs/utils/BasicHashtable.cpp
@@ -80,7 +80,7 @@ void BasicHashtableImpl::clear() {
SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets);
if (sb->onlyOwner()) {
destroyBuckets(mBuckets, mBucketCount);
- for (size_t i = 0; i < mSize; i++) {
+ for (size_t i = 0; i < mBucketCount; i++) {
Bucket& bucket = bucketAt(mBuckets, i);
bucket.cookie = 0;
}
diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp
index 18fd84f..e60f5d8 100644
--- a/libs/utils/CallStack.cpp
+++ b/libs/utils/CallStack.cpp
@@ -30,6 +30,11 @@ CallStack::CallStack() :
mCount(0) {
}
+CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) {
+ this->update(ignoreDepth+1, maxDepth);
+ this->dump(logtag);
+}
+
CallStack::CallStack(const CallStack& rhs) :
mCount(rhs.mCount) {
if (mCount) {
@@ -96,7 +101,7 @@ void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
mCount = count > 0 ? count : 0;
}
-void CallStack::dump(const char* prefix) const {
+void CallStack::dump(const char* logtag, const char* prefix) const {
backtrace_symbol_t symbols[mCount];
get_backtrace_symbols(mStack, mCount, symbols);
@@ -104,7 +109,9 @@ void CallStack::dump(const char* prefix) const {
char line[MAX_BACKTRACE_LINE_LENGTH];
format_backtrace_line(i, &mStack[i], &symbols[i],
line, MAX_BACKTRACE_LINE_LENGTH);
- ALOGD("%s%s", prefix, line);
+ ALOG(LOG_DEBUG, logtag, "%s%s",
+ prefix ? prefix : "",
+ line);
}
free_backtrace_symbols(symbols, mCount);
}
@@ -118,7 +125,9 @@ String8 CallStack::toString(const char* prefix) const {
char line[MAX_BACKTRACE_LINE_LENGTH];
format_backtrace_line(i, &mStack[i], &symbols[i],
line, MAX_BACKTRACE_LINE_LENGTH);
- str.append(prefix);
+ if (prefix) {
+ str.append(prefix);
+ }
str.append(line);
str.append("\n");
}
diff --git a/libs/utils/JenkinsHash.cpp b/libs/utils/JenkinsHash.cpp
new file mode 100644
index 0000000..52c9bb7
--- /dev/null
+++ b/libs/utils/JenkinsHash.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+/* Implementation of Jenkins one-at-a-time hash function. These choices are
+ * optimized for code size and portability, rather than raw speed. But speed
+ * should still be quite good.
+ **/
+
+#include <utils/JenkinsHash.h>
+
+namespace android {
+
+hash_t JenkinsHashWhiten(uint32_t hash) {
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
+ hash = JenkinsHashMix(hash, (uint32_t)size);
+ size_t i;
+ for (i = 0; i < (size & -4); i += 4) {
+ uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
+ hash = JenkinsHashMix(hash, data);
+ }
+ if (size & 3) {
+ uint32_t data = bytes[i];
+ data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0;
+ data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0;
+ hash = JenkinsHashMix(hash, data);
+ }
+ return hash;
+}
+
+uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
+ hash = JenkinsHashMix(hash, (uint32_t)size);
+ size_t i;
+ for (i = 0; i < (size & -2); i += 2) {
+ uint32_t data = shorts[i] | (shorts[i+1] << 16);
+ hash = JenkinsHashMix(hash, data);
+ }
+ if (size & 1) {
+ uint32_t data = shorts[i];
+ hash = JenkinsHashMix(hash, data);
+ }
+ return hash;
+}
+
+}
+
diff --git a/libs/utils/LinearAllocator.cpp b/libs/utils/LinearAllocator.cpp
new file mode 100644
index 0000000..a07a291
--- /dev/null
+++ b/libs/utils/LinearAllocator.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "LinearAllocator"
+#define LOG_NDEBUG 1
+
+#include <stdlib.h>
+#include <utils/LinearAllocator.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now > s_nextLog) {
+ s_nextLog = now + milliseconds_to_nanoseconds(10);
+ ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ }
+}
+
+static void _addAllocation(size_t size) {
+ android::AutoMutex lock(s_mutex);
+ s_totalAllocations += size;
+ _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+namespace android {
+
+class LinearAllocator::Page {
+public:
+ Page* next() { return mNextPage; }
+ void setNext(Page* next) { mNextPage = next; }
+
+ Page()
+ : mNextPage(0)
+ {}
+
+ void* operator new(size_t size, void* buf) { return buf; }
+
+ void* start() {
+ return (void*) (((size_t)this) + sizeof(Page));
+ }
+
+ void* end(int pageSize) {
+ return (void*) (((size_t)start()) + pageSize);
+ }
+
+private:
+ Page(const Page& other) {}
+ Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+ : mPageSize(INITIAL_PAGE_SIZE)
+ , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mNext(0)
+ , mCurrentPage(0)
+ , mPages(0)
+ , mTotalAllocated(0)
+ , mWastedSpace(0)
+ , mPageCount(0)
+ , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+ Page* p = mPages;
+ while (p) {
+ Page* next = p->next();
+ p->~Page();
+ free(p);
+ RM_ALLOCATION(mPageSize);
+ p = next;
+ }
+}
+
+void* LinearAllocator::start(Page* p) {
+ return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+ return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+ return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+ if (fitsInCurrentPage(size)) return;
+
+ if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+ mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mPageSize = ALIGN(mPageSize);
+ }
+ mWastedSpace += mPageSize;
+ Page* p = newPage(mPageSize);
+ if (mCurrentPage) {
+ mCurrentPage->setNext(p);
+ }
+ mCurrentPage = p;
+ if (!mPages) {
+ mPages = mCurrentPage;
+ }
+ mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+ size = ALIGN(size);
+ if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+ ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+ // Allocation is too large, create a dedicated page for the allocation
+ Page* page = newPage(size);
+ mDedicatedPageCount++;
+ page->setNext(mPages);
+ mPages = page;
+ if (!mCurrentPage)
+ mCurrentPage = mPages;
+ return start(page);
+ }
+ ensureNext(size);
+ void* ptr = mNext;
+ mNext = ((char*)mNext) + size;
+ mWastedSpace -= size;
+ return ptr;
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+ // Don't bother rewinding across pages
+ allocSize = ALIGN(allocSize);
+ if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+ && ptr == ((char*)mNext - allocSize)) {
+ mTotalAllocated -= allocSize;
+ mWastedSpace += allocSize;
+ mNext = ptr;
+ }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+ pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+ ADD_ALLOCATION(pageSize);
+ mTotalAllocated += pageSize;
+ mPageCount++;
+ void* buf = malloc(pageSize);
+ return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+ if (value < 2000) {
+ result = value;
+ return "B";
+ }
+ if (value < 2000000) {
+ result = value / 1024.0f;
+ return "KB";
+ }
+ result = value / 1048576.0f;
+ return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+ float prettySize;
+ const char* prettySuffix;
+ prettySuffix = toSize(mTotalAllocated, prettySize);
+ ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+ prettySuffix = toSize(mWastedSpace, prettySize);
+ ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+ (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+ ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace android
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index e80a795..e538f68 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "RefBase"
+// #define LOG_NDEBUG 0
#include <utils/RefBase.h>
@@ -34,10 +35,18 @@
// compile with refcounting debugging enabled
#define DEBUG_REFS 0
-#define DEBUG_REFS_FATAL_SANITY_CHECKS 0
-#define DEBUG_REFS_ENABLED_BY_DEFAULT 1
+
+// whether ref-tracking is enabled by default, if not, trackMe(true, false)
+// needs to be called explicitly
+#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
+
+// whether callstack are collected (significantly slows things down)
#define DEBUG_REFS_CALLSTACK_ENABLED 1
+// folder where stack traces are saved when DEBUG_REFS is enabled
+// this folder needs to exist and be writable
+#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+
// log all reference counting operations
#define PRINT_REFS 0
@@ -95,17 +104,13 @@ public:
bool dumpStack = false;
if (!mRetain && mStrongRefs != NULL) {
dumpStack = true;
-#if DEBUG_REFS_FATAL_SANITY_CHECKS
- LOG_ALWAYS_FATAL("Strong references remain!");
-#else
ALOGE("Strong references remain:");
-#endif
ref_entry* refs = mStrongRefs;
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
- refs->stack.dump();
+ refs->stack.dump(LOG_TAG);
#endif
refs = refs->next;
}
@@ -113,26 +118,20 @@ public:
if (!mRetain && mWeakRefs != NULL) {
dumpStack = true;
-#if DEBUG_REFS_FATAL_SANITY_CHECKS
- LOG_ALWAYS_FATAL("Weak references remain:");
-#else
ALOGE("Weak references remain!");
-#endif
ref_entry* refs = mWeakRefs;
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
- refs->stack.dump();
+ refs->stack.dump(LOG_TAG);
#endif
refs = refs->next;
}
}
if (dumpStack) {
ALOGE("above errors at:");
- CallStack stack;
- stack.update();
- stack.dump();
+ CallStack stack(LOG_TAG);
}
}
@@ -198,8 +197,8 @@ public:
{
char name[100];
- snprintf(name, 100, "/data/%p.stack", this);
- int rc = open(name, O_RDWR | O_CREAT | O_APPEND);
+ snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+ int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
write(rc, text.string(), text.length());
close(rc);
@@ -257,12 +256,6 @@ private:
ref = *refs;
}
-#if DEBUG_REFS_FATAL_SANITY_CHECKS
- LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p"
- "(weakref_type %p) that doesn't exist!",
- id, mBase, this);
-#endif
-
ALOGE("RefBase: removing id %p on RefBase %p"
"(weakref_type %p) that doesn't exist!",
id, mBase, this);
@@ -274,9 +267,7 @@ private:
ref = ref->next;
}
- CallStack stack;
- stack.update();
- stack.dump();
+ CallStack stack(LOG_TAG);
}
}
@@ -440,39 +431,68 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id)
incWeak(id);
weakref_impl* const impl = static_cast<weakref_impl*>(this);
-
int32_t curCount = impl->mStrong;
- ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
- this);
+
+ ALOG_ASSERT(curCount >= 0,
+ "attemptIncStrong called on %p after underflow", this);
+
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
+ // we're in the easy/common case of promoting a weak-reference
+ // from an existing strong reference.
if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
break;
}
+ // the strong count has changed on us, we need to re-assert our
+ // situation.
curCount = impl->mStrong;
}
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
- bool allow;
- if (curCount == INITIAL_STRONG_VALUE) {
- // Attempting to acquire first strong reference... this is allowed
- // if the object does NOT have a longer lifetime (meaning the
- // implementation doesn't need to see this), or if the implementation
- // allows it to happen.
- allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
- || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
+ // we're now in the harder case of either:
+ // - there never was a strong reference on us
+ // - or, all strong references have been released
+ if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ // this object has a "normal" life-time, i.e.: it gets destroyed
+ // when the last strong reference goes away
+ if (curCount <= 0) {
+ // the last strong-reference got released, the object cannot
+ // be revived.
+ decWeak(id);
+ return false;
+ }
+
+ // here, curCount == INITIAL_STRONG_VALUE, which means
+ // there never was a strong-reference, so we can try to
+ // promote this object; we need to do that atomically.
+ while (curCount > 0) {
+ if (android_atomic_cmpxchg(curCount, curCount + 1,
+ &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong count has changed on us, we need to re-assert our
+ // situation (e.g.: another thread has inc/decStrong'ed us)
+ curCount = impl->mStrong;
+ }
+
+ if (curCount <= 0) {
+ // promote() failed, some other thread destroyed us in the
+ // meantime (i.e.: strong count reached zero).
+ decWeak(id);
+ return false;
+ }
} else {
- // Attempting to revive the object... this is allowed
- // if the object DOES have a longer lifetime (so we can safely
- // call the object with only a weak ref) and the implementation
- // allows it to happen.
- allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
- && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
- }
- if (!allow) {
- decWeak(id);
- return false;
+ // this object has an "extended" life-time, i.e.: it can be
+ // revived from a weak-reference only.
+ // Ask the object's implementation if it agrees to be revived
+ if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
+ // it didn't so give-up.
+ decWeak(id);
+ return false;
+ }
+ // grab a strong-reference, which is always safe due to the
+ // extended life-time.
+ curCount = android_atomic_inc(&impl->mStrong);
}
- curCount = android_atomic_inc(&impl->mStrong);
// If the strong reference count has already been incremented by
// someone else, the implementor of onIncStrongAttempted() is holding
@@ -490,11 +510,23 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id)
ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif
- if (curCount == INITIAL_STRONG_VALUE) {
- android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
- impl->mBase->onFirstRef();
+ // now we need to fix-up the count if it was INITIAL_STRONG_VALUE
+ // this must be done safely, i.e.: handle the case where several threads
+ // were here in attemptIncStrong().
+ curCount = impl->mStrong;
+ while (curCount >= INITIAL_STRONG_VALUE) {
+ ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,
+ "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
+ this);
+ if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
+ &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong-count changed on us, we need to re-assert the situation,
+ // for e.g.: it's possible the fix-up happened in another thread.
+ curCount = impl->mStrong;
}
-
+
return true;
}
@@ -595,21 +627,27 @@ void RefBase::onLastWeakRef(const void* /*id*/)
// ---------------------------------------------------------------------------
-void RefBase::moveReferences(void* dst, void const* src, size_t n,
- const ReferenceConverterBase& caster)
-{
+void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) {
#if DEBUG_REFS
- const size_t itemSize = caster.getReferenceTypeSize();
for (size_t i=0 ; i<n ; i++) {
- void* d = reinterpret_cast<void *>(intptr_t(dst) + i*itemSize);
- void const* s = reinterpret_cast<void const*>(intptr_t(src) + i*itemSize);
- RefBase* ref(reinterpret_cast<RefBase*>(caster.getReferenceBase(d)));
- ref->mRefs->renameStrongRefId(s, d);
- ref->mRefs->renameWeakRefId(s, d);
+ renamer(i);
}
#endif
}
+void RefBase::renameRefId(weakref_type* ref,
+ const void* old_id, const void* new_id) {
+ weakref_impl* const impl = static_cast<weakref_impl*>(ref);
+ impl->renameStrongRefId(old_id, new_id);
+ impl->renameWeakRefId(old_id, new_id);
+}
+
+void RefBase::renameRefId(RefBase* ref,
+ const void* old_id, const void* new_id) {
+ ref->mRefs->renameStrongRefId(old_id, new_id);
+ ref->mRefs->renameWeakRefId(old_id, new_id);
+}
+
// ---------------------------------------------------------------------------
TextOutput& printStrongPointer(TextOutput& to, const void* val)
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index a25a81f..7b877e0 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -109,30 +109,34 @@ struct thread_data_t {
}
if (name) {
-#if defined(HAVE_PRCTL)
- // Mac OS doesn't have this, and we build libutil for the host too
- int hasAt = 0;
- int hasDot = 0;
- char *s = name;
- while (*s) {
- if (*s == '.') hasDot = 1;
- else if (*s == '@') hasAt = 1;
- s++;
- }
- int len = s - name;
- if (len < 15 || hasAt || !hasDot) {
- s = name;
- } else {
- s = name + len - 15;
- }
- prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
-#endif
+ androidSetThreadName(name);
free(name);
}
return f(u);
}
};
+void androidSetThreadName(const char* name) {
+#if defined(HAVE_PRCTL)
+ // Mac OS doesn't have this, and we build libutil for the host too
+ int hasAt = 0;
+ int hasDot = 0;
+ const char *s = name;
+ while (*s) {
+ if (*s == '.') hasDot = 1;
+ else if (*s == '@') hasAt = 1;
+ s++;
+ }
+ int len = s - name;
+ if (len < 15 || hasAt || !hasDot) {
+ s = name;
+ } else {
+ s = name + len - 15;
+ }
+ prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#endif
+}
+
int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName,
@@ -871,6 +875,11 @@ status_t Thread::join()
return mStatus;
}
+bool Thread::isRunning() const {
+ Mutex::Autolock _l(mLock);
+ return mRunning;
+}
+
#ifdef HAVE_ANDROID_OS
pid_t Thread::getTid() const
{
diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp
index f5aaea3..36fd802 100644
--- a/libs/utils/Trace.cpp
+++ b/libs/utils/Trace.cpp
@@ -14,52 +14,12 @@
* limitations under the License.
*/
-#define LOG_TAG "Trace"
-
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Trace.h>
#include <utils/misc.h>
+#include <utils/Trace.h>
-namespace android {
-
-volatile int32_t Tracer::sIsReady = 0;
-int Tracer::sTraceFD = -1;
-uint64_t Tracer::sEnabledTags = ATRACE_TAG_NOT_READY;
-Mutex Tracer::sMutex;
-
-void Tracer::changeCallback() {
- Mutex::Autolock lock(sMutex);
- if (sIsReady && sTraceFD >= 0) {
- loadSystemProperty();
- }
-}
-
-void Tracer::init() {
- Mutex::Autolock lock(sMutex);
-
- if (!sIsReady) {
- add_sysprop_change_callback(changeCallback, 0);
-
- const char* const traceFileName =
- "/sys/kernel/debug/tracing/trace_marker";
- sTraceFD = open(traceFileName, O_WRONLY);
- if (sTraceFD == -1) {
- ALOGE("error opening trace file: %s (%d)", strerror(errno), errno);
- sEnabledTags = 0; // no tracing can occur
- } else {
- loadSystemProperty();
- }
-
- android_atomic_release_store(1, &sIsReady);
- }
-}
+static void traceInit() __attribute__((constructor));
-void Tracer::loadSystemProperty() {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.atrace.tags.enableflags", value, "0");
- sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK)
- | ATRACE_TAG_ALWAYS;
+static void traceInit()
+{
+ ::android::add_sysprop_change_callback(atrace_update_tags, 0);
}
-
-} // namespace andoid
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index c3257bb..70f49de 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -343,6 +343,16 @@ ssize_t VectorImpl::setCapacity(size_t new_capacity)
return new_capacity;
}
+ssize_t VectorImpl::resize(size_t size) {
+ ssize_t result = NO_ERROR;
+ if (size > mCount) {
+ result = insertAt(mCount, size - mCount);
+ } else if (size < mCount) {
+ result = removeItemsAt(size, mCount - size);
+ }
+ return result < 0 ? result : size;
+}
+
void VectorImpl::release_storage()
{
if (mStorage) {
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 5b2b5b1..a2ca9c8 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -7,6 +7,7 @@ test_src_files := \
BasicHashtable_test.cpp \
BlobCache_test.cpp \
Looper_test.cpp \
+ LruCache_test.cpp \
String8_test.cpp \
Unicode_test.cpp \
Vector_test.cpp \
diff --git a/libs/utils/tests/LruCache_test.cpp b/libs/utils/tests/LruCache_test.cpp
new file mode 100644
index 0000000..e573952
--- /dev/null
+++ b/libs/utils/tests/LruCache_test.cpp
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+typedef int SimpleKey;
+typedef const char* StringValue;
+
+struct ComplexKey {
+ int k;
+
+ explicit ComplexKey(int k) : k(k) {
+ instanceCount += 1;
+ }
+
+ ComplexKey(const ComplexKey& other) : k(other.k) {
+ instanceCount += 1;
+ }
+
+ ~ComplexKey() {
+ instanceCount -= 1;
+ }
+
+ bool operator ==(const ComplexKey& other) const {
+ return k == other.k;
+ }
+
+ bool operator !=(const ComplexKey& other) const {
+ return k != other.k;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+template<> inline hash_t hash_type(const ComplexKey& value) {
+ return hash_type(value.k);
+}
+
+struct ComplexValue {
+ int v;
+
+ explicit ComplexValue(int v) : v(v) {
+ instanceCount += 1;
+ }
+
+ ComplexValue(const ComplexValue& other) : v(other.v) {
+ instanceCount += 1;
+ }
+
+ ~ComplexValue() {
+ instanceCount -= 1;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+typedef LruCache<ComplexKey, ComplexValue> ComplexCache;
+
+class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
+public:
+ EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+ ~EntryRemovedCallback() {}
+ void operator()(SimpleKey& k, StringValue& v) {
+ callbackCount += 1;
+ lastKey = k;
+ lastValue = v;
+ }
+ ssize_t callbackCount;
+ SimpleKey lastKey;
+ StringValue lastValue;
+};
+
+class LruCacheTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ ComplexKey::instanceCount = 0;
+ ComplexValue::instanceCount = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+ }
+
+ void assertInstanceCount(ssize_t keys, ssize_t values) {
+ if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+ FAIL() << "Expected " << keys << " keys and " << values << " values "
+ "but there were actually " << ComplexKey::instanceCount << " keys and "
+ << ComplexValue::instanceCount << " values";
+ }
+ }
+};
+
+TEST_F(LruCacheTest, Empty) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ EXPECT_EQ(NULL, cache.get(0));
+ EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(LruCacheTest, Simple) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(3u, cache.size());
+}
+
+TEST_F(LruCacheTest, MaxCapacity) {
+ LruCache<SimpleKey, StringValue> cache(2);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, RemoveLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ cache.removeOldest();
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, GetUpdatesLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ cache.removeOldest();
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_EQ(NULL, cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+uint32_t hash_int(int x) {
+ return JenkinsHashWhiten(JenkinsHashMix(0, x));
+}
+
+TEST_F(LruCacheTest, StressTest) {
+ const size_t kCacheSize = 512;
+ LruCache<SimpleKey, StringValue> cache(512);
+ const size_t kNumKeys = 16 * 1024;
+ const size_t kNumIters = 100000;
+ char* strings[kNumKeys];
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ strings[i] = (char *)malloc(16);
+ sprintf(strings[i], "%d", i);
+ }
+
+ srandom(12345);
+ int hitCount = 0;
+ for (size_t i = 0; i < kNumIters; i++) {
+ int index = random() % kNumKeys;
+ uint32_t key = hash_int(index);
+ const char *val = cache.get(key);
+ if (val != NULL) {
+ EXPECT_EQ(strings[index], val);
+ hitCount++;
+ } else {
+ cache.put(key, strings[index]);
+ }
+ }
+ size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;
+ EXPECT_LT(int(expectedHitCount * 0.9), hitCount);
+ EXPECT_GT(int(expectedHitCount * 1.1), hitCount);
+ EXPECT_EQ(kCacheSize, cache.size());
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ free((void *)strings[i]);
+ }
+}
+
+TEST_F(LruCacheTest, NoLeak) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3); // the null value counts as an instance
+}
+
+TEST_F(LruCacheTest, Clear) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+}
+
+TEST_F(LruCacheTest, ClearNoDoubleFree) {
+ {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.removeOldest();
+ cache.clear();
+ assertInstanceCount(0, 1);
+ }
+ assertInstanceCount(0, 0);
+}
+
+TEST_F(LruCacheTest, ClearReuseOk) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+}
+
+TEST_F(LruCacheTest, Callback) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.removeOldest();
+ EXPECT_EQ(1, callback.callbackCount);
+ EXPECT_EQ(1, callback.lastKey);
+ EXPECT_STREQ("one", callback.lastValue);
+}
+
+TEST_F(LruCacheTest, CallbackOnClear) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.clear();
+ EXPECT_EQ(3, callback.callbackCount);
+}
+
+}