Merge "Allow child layers to inherit color transform from their parent"
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 59d67f3..cfd2b40 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -769,7 +769,13 @@
/** all apps */
AKEYCODE_ALL_APPS = 284,
/** refresh key */
- AKEYCODE_REFRESH = 285
+ AKEYCODE_REFRESH = 285,
+ /** Thumbs up key. Apps can use this to let user upvote content. */
+ AKEYCODE_THUMBS_UP = 286,
+ /** Thumbs down key. Apps can use this to let user downvote content. */
+ AKEYCODE_THUMBS_DOWN = 287,
+ /** Consumed by system to switch current viewer profile. */
+ AKEYCODE_PROFILE_SWITCH = 288
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 6d072a3..59d16d1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -325,6 +325,9 @@
DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
DEFINE_KEYCODE(ALL_APPS),
DEFINE_KEYCODE(REFRESH),
+ DEFINE_KEYCODE(THUMBS_UP),
+ DEFINE_KEYCODE(THUMBS_DOWN),
+ DEFINE_KEYCODE(PROFILE_SWITCH),
{ nullptr, 0 }
};
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 28d0e4f..49a9414 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -89,6 +89,15 @@
return false;
}
+int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& callingPackage)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->getUidProcessState(uid, callingPackage);
+ }
+ return PROCESS_STATE_UNKNOWN;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 428db4d..377f604 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,8 +17,8 @@
#include <unistd.h>
#include <fcntl.h>
+#include <binder/ActivityManager.h>
#include <binder/IActivityManager.h>
-
#include <binder/Parcel.h>
namespace android {
@@ -90,6 +90,20 @@
if (reply.readExceptionCode() != 0) return false;
return reply.readInt32() == 1;
}
+
+ virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(uid);
+ data.writeString16(callingPackage);
+ remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) {
+ return ActivityManager::PROCESS_STATE_UNKNOWN;
+ }
+ return reply.readInt32();
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 697e948..82f9047 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -55,6 +55,16 @@
data.writeInt32(disabled ? 1 : 0);
remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
+ data.writeInt32((int32_t) uid);
+ data.writeInt32(procState);
+ data.writeInt64(procStateSeq);
+ remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
// ----------------------------------------------------------------------
@@ -89,6 +99,14 @@
onUidIdle(uid, disabled);
return NO_ERROR;
} break;
+ case ON_UID_STATE_CHANGED_TRANSACTION: {
+ CHECK_INTERFACE(IUidObserver, data, reply);
+ uid_t uid = data.readInt32();
+ int32_t procState = data.readInt32();
+ int64_t procStateSeq = data.readInt64();
+ onUidStateChanged(uid, procState, procStateSeq);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index b8db091..26dafd0 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -31,6 +31,8 @@
public:
enum {
+ // Flag for registerUidObserver: report uid state changed
+ UID_OBSERVER_PROCSTATE = 1<<0,
// Flag for registerUidObserver: report uid gone
UID_OBSERVER_GONE = 1<<1,
// Flag for registerUidObserver: report uid has become idle
@@ -40,8 +42,27 @@
};
enum {
- // Not a real process state
- PROCESS_STATE_UNKNOWN = -1
+ PROCESS_STATE_UNKNOWN = -1,
+ PROCESS_STATE_PERSISTENT = 0,
+ PROCESS_STATE_PERSISTENT_UI = 1,
+ PROCESS_STATE_TOP = 2,
+ PROCESS_STATE_FOREGROUND_SERVICE = 3,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 5,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 6,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 7,
+ PROCESS_STATE_BACKUP = 8,
+ PROCESS_STATE_SERVICE = 9,
+ PROCESS_STATE_RECEIVER = 10,
+ PROCESS_STATE_TOP_SLEEPING = 11,
+ PROCESS_STATE_HEAVY_WEIGHT = 12,
+ PROCESS_STATE_HOME = 13,
+ PROCESS_STATE_LAST_ACTIVITY = 14,
+ PROCESS_STATE_CACHED_ACTIVITY = 15,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16,
+ PROCESS_STATE_CACHED_RECENT = 17,
+ PROCESS_STATE_CACHED_EMPTY = 18,
+ PROCESS_STATE_NONEXISTENT = 19,
};
ActivityManager();
@@ -53,8 +74,10 @@
const String16& callingPackage);
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
+ int getUidProcessState(const uid_t uid, const String16& callingPackage);
- status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+
+ status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
private:
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index f34969b..6abc071 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -38,12 +38,14 @@
const String16& callingPackage) = 0;
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
+ virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
- IS_UID_ACTIVE_TRANSACTION
+ IS_UID_ACTIVE_TRANSACTION,
+ GET_UID_PROCESS_STATE_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 75b348c..26e8c0b 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -132,8 +132,10 @@
bool isServingCall() const;
// The work source represents the UID of the process we should attribute the transaction
- // to.
- // We use -1 to specify that the work source was not set using #setWorkSource.
+ // to. We use -1 to specify that the work source was not set using #setWorkSource.
+ //
+ // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
+ // side.
static const int32_t kUnsetWorkSource = -1;
private:
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index 9937ad6..a1f530d 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,11 +34,13 @@
virtual void onUidGone(uid_t uid, bool disabled) = 0;
virtual void onUidActive(uid_t uid) = 0;
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
ON_UID_ACTIVE_TRANSACTION,
- ON_UID_IDLE_TRANSACTION
+ ON_UID_IDLE_TRANSACTION,
+ ON_UID_STATE_CHANGED_TRANSACTION
};
};
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 91a5053..d799c5f 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,8 +39,6 @@
"libutils",
],
- cpp_std: "c++17",
-
version_script: "libbinder_ndk.map.txt",
}
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index 67481cf..b29b6e7 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -22,7 +22,6 @@
strip: {
none: true,
},
- cpp_std: "c++17",
cflags: [
"-O0",
"-g",
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8cb8649..fc676f1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -16,7 +16,6 @@
cc_library {
name: "libinput",
- cpp_std: "c++17",
host_supported: true,
cflags: [
"-Wall",
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index fba319d..d847884 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -51,10 +51,6 @@
"-Wno-unused-function",
],
- cppflags: [
- "-std=c++1z"
- ],
-
version_script: "libnativewindow.map.txt",
srcs: [
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 8150931..618c7d6 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -325,14 +325,14 @@
return *this;
}
-Region& Region::scaleSelf(int sx, int sy) {
+Region& Region::scaleSelf(float sx, float sy) {
size_t count = mStorage.size();
Rect* rects = mStorage.editArray();
while (count) {
- rects->left *= sx;
- rects->right *= sx;
- rects->top *= sy;
- rects->bottom *= sy;
+ rects->left = static_cast<int32_t>(rects->left * sx + 0.5f);
+ rects->right = static_cast<int32_t>(rects->right * sx + 0.5f);
+ rects->top = static_cast<int32_t>(rects->top * sy + 0.5f);
+ rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f);
rects++;
count--;
}
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index c5e31c5..0a09960 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -89,7 +89,7 @@
// these translate rhs first
Region& translateSelf(int dx, int dy);
- Region& scaleSelf(int sx, int sy);
+ Region& scaleSelf(float sx, float sy);
Region& orSelf(const Region& rhs, int dx, int dy);
Region& xorSelf(const Region& rhs, int dx, int dy);
Region& andSelf(const Region& rhs, int dx, int dy);
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index b7ad4e5..00f30a6 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -53,6 +53,7 @@
],
shared_libs: [
"android.frameworks.bufferhub@1.0",
+ "libcutils",
"libhidlbase",
"libhwbinder",
"libpdx_default_transport",
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index d30636f..7c85e66 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -19,6 +19,7 @@
#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
#include <gtest/gtest.h>
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
@@ -35,7 +36,7 @@
const int kUsage = 0;
const size_t kUserMetadataSize = 0;
-using dvr::BufferHubDefs::IsBufferGained;
+using dvr::BufferHubDefs::IsBufferReleased;
using dvr::BufferHubDefs::kFirstClientBitMask;
using dvr::BufferHubDefs::kMetadataHeaderSize;
using frameworks::bufferhub::V1_0::BufferHubStatus;
@@ -118,9 +119,9 @@
// We use client_state_mask() to tell those two instances apart.
EXPECT_NE(bufferStateMask1, bufferStateMask2);
- // Both buffer instances should be in gained state.
- EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
- EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+ // Both buffer instances should be in released state currently.
+ EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
// TODO(b/112338294): rewrite test after migration
return;
@@ -144,7 +145,7 @@
EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
}
-TEST_F(BufferHubBufferTest, DuplicateBuffer) {
+TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
// TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
sp<IBufferHub> bufferhub = IBufferHub::getService();
ASSERT_NE(nullptr, bufferhub.get());
@@ -170,11 +171,63 @@
token = outToken;
ret = status;
};
- EXPECT_TRUE(client->duplicate(dup_cb).isOk());
+ ASSERT_TRUE(client->duplicate(dup_cb).isOk());
EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
ASSERT_NE(token.getNativeHandle(), nullptr);
EXPECT_EQ(token->numInts, 1);
EXPECT_EQ(token->numFds, 0);
+
+ sp<IBufferClient> client2;
+ IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+ ret = status;
+ client2 = outClient;
+ };
+ ASSERT_TRUE(bufferhub->importBuffer(token, import_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
+ EXPECT_NE(nullptr, client2.get());
+ // TODO(b/116681016): once BufferNode.id() is exposed via BufferHubBuffer, check origin.id =
+ // improted.id here.
+}
+
+// nullptr must not crash the service
+TEST_F(BufferHubBufferTest, ImportNullToken) {
+ // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ ASSERT_NE(nullptr, bufferhub.get());
+
+ hidl_handle nullToken;
+ sp<IBufferClient> client;
+ BufferHubStatus ret;
+ IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+ client = outClient;
+ ret = status;
+ };
+ ASSERT_TRUE(bufferhub->importBuffer(nullToken, import_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
+ EXPECT_EQ(nullptr, client.get());
+}
+
+// This test has a very little chance to fail (number of existing tokens / 2 ^ 32)
+TEST_F(BufferHubBufferTest, ImportInvalidToken) {
+ // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ ASSERT_NE(nullptr, bufferhub.get());
+
+ native_handle_t* tokenHandle = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+ tokenHandle->data[0] = 0;
+
+ hidl_handle invalidToken(tokenHandle);
+ sp<IBufferClient> client;
+ BufferHubStatus ret;
+ IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+ client = outClient;
+ ret = status;
+ };
+ ASSERT_TRUE(bufferhub->importBuffer(invalidToken, import_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
+ EXPECT_EQ(nullptr, client.get());
+
+ native_handle_delete(tokenHandle);
}
} // namespace
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
index 70f86b3..14422bf 100644
--- a/libs/ui/tests/BufferHubMetadata_test.cpp
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -17,7 +17,7 @@
#include <gtest/gtest.h>
#include <ui/BufferHubMetadata.h>
-using android::dvr::BufferHubDefs::IsBufferGained;
+using android::dvr::BufferHubDefs::IsBufferReleased;
namespace android {
namespace dvr {
@@ -52,19 +52,13 @@
BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
EXPECT_NE(mh1, nullptr);
- // TODO(b/111976433): Update this test once BufferHub state machine gets
- // updated. In the old model, buffer starts in the gained state (i.e.
- // valued 0). In the new model, buffer states in the released state.
- EXPECT_TRUE(IsBufferGained(mh1->fence_state.load()));
+ EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load()));
EXPECT_TRUE(m2.IsValid());
BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
EXPECT_NE(mh2, nullptr);
- // TODO(b/111976433): Update this test once BufferHub state machine gets
- // updated. In the old model, buffer starts in the gained state (i.e.
- // valued 0). In the new model, buffer states in the released state.
- EXPECT_TRUE(IsBufferGained(mh2->fence_state.load()));
+ EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load()));
}
TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index 73ca69b..9bcfaa1 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -23,11 +23,13 @@
using android::sp;
using android::dvr::ConsumerBuffer;
using android::dvr::ProducerBuffer;
-using android::dvr::BufferHubDefs::IsBufferAcquired;
-using android::dvr::BufferHubDefs::IsBufferGained;
-using android::dvr::BufferHubDefs::IsBufferPosted;
+using android::dvr::BufferHubDefs::AnyClientAcquired;
+using android::dvr::BufferHubDefs::AnyClientGained;
+using android::dvr::BufferHubDefs::AnyClientPosted;
using android::dvr::BufferHubDefs::IsBufferReleased;
-using android::dvr::BufferHubDefs::kConsumerStateMask;
+using android::dvr::BufferHubDefs::IsClientAcquired;
+using android::dvr::BufferHubDefs::IsClientPosted;
+using android::dvr::BufferHubDefs::IsClientReleased;
using android::dvr::BufferHubDefs::kFirstClientBitMask;
using android::dvr::BufferHubDefs::kMetadataHeaderSize;
using android::pdx::LocalChannelHandle;
@@ -52,58 +54,49 @@
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<ConsumerBuffer> c =
+ std::unique_ptr<ConsumerBuffer> c1 =
ConsumerBuffer::Import(p->CreateConsumer());
- ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_TRUE(c1.get() != nullptr);
// Check that consumers can spawn other consumers.
std::unique_ptr<ConsumerBuffer> c2 =
- ConsumerBuffer::Import(c->CreateConsumer());
+ ConsumerBuffer::Import(c1->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
- // Producer state mask is unique, i.e. 1.
+ // Checks the state masks of client p, c1 and c2.
EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask);
- // Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c->client_state_mask() & kFirstClientBitMask, 0U);
- // Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c->client_state_mask(), 0U);
- EXPECT_EQ(c->client_state_mask() & (c->client_state_mask() - 1), 0U);
- // Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c2->client_state_mask() & kFirstClientBitMask, 0U);
- // Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c2->client_state_mask(), 0U);
- EXPECT_EQ(c2->client_state_mask() & (c2->client_state_mask() - 1), 0U);
- // Each consumer should have unique bit.
- EXPECT_EQ(c->client_state_mask() & c2->client_state_mask(), 0U);
+ EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1);
+ EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2);
// Initial state: producer not available, consumers not available.
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->Post(LocalHandle()));
// New state: producer not available, consumers available.
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(1, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
LocalHandle fence;
- EXPECT_EQ(0, c->Acquire(&fence));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->Acquire(&fence));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, c2->Acquire(&fence));
EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_EQ(0, c1->Release(LocalHandle()));
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, c2->Discard());
-
EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
EXPECT_EQ(0, p->Gain(&fence));
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
}
@@ -144,7 +137,8 @@
// No events should be signaled initially.
ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0));
- // Post the producer and check for consumer signal.
+ // Gain and post the producer and check for consumer signal.
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->Post({}));
ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(),
kPollTimeoutMs));
@@ -189,7 +183,7 @@
EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
client_state_masks |= cs[i]->client_state_mask();
}
- EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask);
+ EXPECT_EQ(client_state_masks, ~0ULL);
// The 64th creation will fail with out-of-memory error.
auto state = p->CreateConsumer();
@@ -204,7 +198,6 @@
// The released state mask will be reused.
EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
client_state_masks |= cs[i]->client_state_mask();
- EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask);
}
}
@@ -217,24 +210,20 @@
ASSERT_TRUE(c.get() != nullptr);
LocalHandle fence;
+ EXPECT_EQ(0, p->GainAsync());
- // The producer buffer starts in gained state.
-
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EALREADY, p->Gain(&fence));
// Post in gained state should succeed.
EXPECT_EQ(0, p->Post(LocalHandle()));
- // Post, release, and gain in posted state should fail.
+ // Post and gain in posted state should fail.
EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
EXPECT_EQ(-EBUSY, p->Gain(&fence));
// Acquire in posted state should succeed.
- EXPECT_LE(0, c->Acquire(&fence));
+ EXPECT_EQ(0, c->Acquire(&fence));
// Acquire, post, and gain in acquired state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
@@ -245,18 +234,15 @@
EXPECT_EQ(0, c->Release(LocalHandle()));
EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- // Release, acquire, and post in released state should fail.
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
+ // Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
// Gain in released state should succeed.
EXPECT_EQ(0, p->Gain(&fence));
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EALREADY, p->Gain(&fence));
}
TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
@@ -269,24 +255,20 @@
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
+ EXPECT_EQ(0, p->GainAsync());
- // The producer buffer starts in gained state.
-
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
// Post in gained state should succeed.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
- // Post, release, and gain in posted state should fail.
+ // Post and gain in posted state should fail.
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
@@ -295,7 +277,7 @@
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ EXPECT_TRUE(AnyClientAcquired(p->buffer_state()));
// Acquire, post, and gain in acquired state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -310,8 +292,7 @@
EXPECT_EQ(p->buffer_state(), c->buffer_state());
EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
- // Release, acquire, and post in released state should fail.
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ // Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
@@ -320,23 +301,32 @@
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
- // Acquire, release, and gain in gained state should fail.
+ // Acquire and gain in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_FALSE(invalid_fence.IsValid());
+}
+
+TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->GainAsync());
}
TEST_F(LibBufferHubTest, TestGainPostedBuffer) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
-
- // The producer buffer starts in gained state. Post the buffer.
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
ASSERT_EQ(0, p->Post(LocalHandle()));
+ ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
// Gain in posted state should only succeed with gain_posted_buffer = true.
LocalHandle invalid_fence;
@@ -348,9 +338,12 @@
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
-
- // The producer buffer starts in gained state. Post the buffer.
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
ASSERT_EQ(0, p->Post(LocalHandle()));
+ ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
// GainAsync in posted state should only succeed with gain_posted_buffer
// equals true.
@@ -360,54 +353,49 @@
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true));
}
-TEST_F(LibBufferHubTest, TestZeroConsumer) {
+TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->Post(LocalHandle()));
+ // Producer state bit is in released state after post. The overall state of
+ // the buffer is also released because there is no consumer of this buffer.
+ ASSERT_TRUE(IsBufferReleased(p->buffer_state()));
- DvrNativeBufferMetadata metadata;
+ // Gain in released state should succeed.
LocalHandle invalid_fence;
-
- // Newly created.
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
- EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
-
- // The buffer should stay in posted stay until a consumer picks it up.
- EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
- // A new consumer should still be able to acquire the buffer immediately.
- std::unique_ptr<ConsumerBuffer> c =
- ConsumerBuffer::Import(p->CreateConsumer());
- ASSERT_TRUE(c.get() != nullptr);
- EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_EQ(0, p->Gain(&invalid_fence, false));
}
TEST_F(LibBufferHubTest, TestMaxConsumers) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
+ uint64_t producer_state_mask = p->client_state_mask();
std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
- for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ for (size_t i = 0; i < kMaxConsumerCount; ++i) {
cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
- EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(cs[i]->buffer_state()));
+ EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
}
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the producer should trigger all consumers to be available.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
- for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
+ for (size_t i = 0; i < kMaxConsumerCount; ++i) {
EXPECT_TRUE(
- IsBufferPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
+ IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ EXPECT_TRUE(
+ IsClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
}
// All consumers have to release before the buffer is considered to be
@@ -430,44 +418,57 @@
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+ EXPECT_TRUE(AnyClientGained(c->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer should signal already created consumer.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_TRUE(AnyClientAcquired(c->buffer_state()));
}
-TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
+TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer before any consumer gets created.
+ // The buffer should be in released state because it is not expected to be
+ // read by any clients.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- // Newly created consumer should be automatically sigalled.
+ // Newly created consumer will not be signalled for the posted buffer before
+ // its creation. It cannot acquire the buffer immediately.
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+ EXPECT_FALSE(IsClientPosted(c->buffer_state(), c->client_state_mask()));
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+
+ // Producer should be able to gain back and post the buffer
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+
+ // Consumer should be able to pick up the buffer this time.
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_TRUE(IsClientAcquired(c->buffer_state(), c->client_state_mask()));
}
TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
@@ -479,6 +480,7 @@
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c1.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
@@ -503,7 +505,7 @@
EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
}
TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -517,6 +519,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata)));
EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
@@ -545,6 +548,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
// It is illegal to post metadata larger than originally requested during
// buffer allocation.
@@ -573,6 +577,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m)));
@@ -598,6 +603,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
int64_t sequence = 3;
EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
@@ -613,6 +619,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
LocalHandle fence;
@@ -627,6 +634,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
int64_t sequence = 3;
EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
@@ -648,6 +656,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
@@ -711,44 +720,94 @@
ASSERT_TRUE(c1.get() != nullptr);
const uint64_t client_state_mask1 = c1->client_state_mask();
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
LocalHandle fence;
EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_LE(0, c1->AcquireAsync(&meta, &fence));
- // Destroy the consumer now will make it orphaned and the buffer is still
- // acquired.
- c1 = nullptr;
- EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence));
+ // Destroy the consumer who has acquired but not released the buffer.
+ c1 = nullptr;
+
+ // The buffer is now available for the producer to gain.
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+ // Newly added consumer is not able to acquire the buffer.
std::unique_ptr<ConsumerBuffer> c2 =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
const uint64_t client_state_mask2 = c2->client_state_mask();
EXPECT_NE(client_state_mask1, client_state_mask2);
+ EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence));
- // The new consumer is available for acquire.
+ // Producer should be able to gain.
+ EXPECT_EQ(0, p->GainAsync(&meta, &fence, false));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireLastPosted) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<ConsumerBuffer> c1 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c1.get() != nullptr);
+ const uint64_t client_state_mask1 = c1->client_state_mask();
+
+ EXPECT_EQ(0, p->GainAsync());
+ DvrNativeBufferMetadata meta;
+ EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
+ EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+
+ // c2 is created when the buffer is in posted state. buffer state for c1 is
+ // posted. Thus, c2 should be automatically set to posted and able to acquire.
+ std::unique_ptr<ConsumerBuffer> c2 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+ const uint64_t client_state_mask2 = c2->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask2);
EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_LE(0, c2->AcquireAsync(&meta, &fence));
- // Releasing the consumer makes the buffer gainable.
- EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle()));
+ LocalHandle invalid_fence;
+ EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence));
- // The buffer is now available for the producer to gain.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence));
- // But if another consumer is created in released state.
+ // c3 is created when the buffer is in acquired state. buffer state for c1 and
+ // c2 are acquired. Thus, c3 should be automatically set to posted and able to
+ // acquire.
std::unique_ptr<ConsumerBuffer> c3 =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c3.get() != nullptr);
const uint64_t client_state_mask3 = c3->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask3);
EXPECT_NE(client_state_mask2, client_state_mask3);
- // The consumer buffer is not acquirable.
- EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
- EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence));
+ EXPECT_LT(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence));
- // Producer should be able to gain no matter what.
- EXPECT_EQ(0, p->GainAsync(&meta, &fence));
+ // Releasing c2 and c3 in normal ways.
+ EXPECT_EQ(0, c2->Release(LocalHandle()));
+ EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle()));
+
+ // Destroy the c1 who has not released the buffer.
+ c1 = nullptr;
+
+ // The buffer is now available for the producer to gain.
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+ // C4 is created in released state. Thus, it cannot gain the just posted
+ // buffer.
+ std::unique_ptr<ConsumerBuffer> c4 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c4.get() != nullptr);
+ const uint64_t client_state_mask4 = c4->client_state_mask();
+ EXPECT_NE(client_state_mask3, client_state_mask4);
+ EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence));
+
+ // Producer should be able to gain.
+ EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence));
}
TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
@@ -767,6 +826,7 @@
int p_id = p->id();
// Detach in posted state should fail.
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
auto s1 = p->Detach();
@@ -869,7 +929,8 @@
ASSERT_TRUE(p1.get() != nullptr);
int p1_id = p1->id();
- // Detached the producer.
+ // Detached the producer from gained state.
+ EXPECT_EQ(0, p1->GainAsync());
auto status_or_handle = p1->Detach();
EXPECT_TRUE(status_or_handle.ok());
LocalChannelHandle h1 = status_or_handle.take();
@@ -919,8 +980,8 @@
EXPECT_NE(b1->client_state_mask(), b2->client_state_mask());
// Both buffer instances should be in gained state.
- EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
- EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
// TODO(b/112338294) rewrite test after migration
return;
diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp
index 68cc766..2dc427a 100644
--- a/libs/vr/libbufferhub/buffer_hub_base.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_base.cpp
@@ -26,6 +26,8 @@
cid_(-1) {}
BufferHubBase::~BufferHubBase() {
+ // buffer_state and fence_state are not reset here. They will be used to
+ // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method.
if (metadata_header_ != nullptr) {
metadata_buffer_.Unlock();
}
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
index 8e630ec..62fb5fd 100644
--- a/libs/vr/libbufferhub/consumer_buffer.cpp
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -35,17 +35,41 @@
if (!out_meta)
return -EINVAL;
- // Only check producer bit and this consumer buffer's particular consumer bit.
- // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
- // is not set.
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferPosted(buffer_state, client_state_mask())) {
- ALOGE("ConsumerBuffer::LocalAcquire: not posted, id=%d state=%" PRIx64
- " client_state_mask=%" PRIx64 ".",
- id(), buffer_state, client_state_mask());
+ // The buffer can be acquired iff the buffer state for this client is posted.
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to acquire the buffer. The buffer is not posted, id=%d "
+ "state=%" PRIx64 " client_state_mask=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state, client_state_mask());
return -EBUSY;
}
+ // Change the buffer state for this consumer from posted to acquired.
+ uint64_t updated_buffer_state = current_buffer_state ^ client_state_mask();
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s Failed to acquire the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to acquire the buffer and modify the buffer state to "
+ "%" PRIx64 ". About to try again if the buffer is still posted.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to acquire the buffer. The buffer is no longer posted, "
+ "id=%d state=%" PRIx64 " client_state_mask=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state, client_state_mask());
+ return -EBUSY;
+ }
+ // The failure of compare_exchange_weak updates current_buffer_state.
+ updated_buffer_state = current_buffer_state ^ client_state_mask();
+ }
+
// Copy the canonical metadata.
void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
@@ -64,8 +88,6 @@
*out_fence = shared_acquire_fence_.Duplicate();
}
- // Set the consumer bit unique to this consumer.
- BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, client_state_mask());
return 0;
}
@@ -118,12 +140,26 @@
if (const int error = CheckMetadata(meta->user_metadata_size))
return error;
- // Check invalid state transition.
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
- ALOGE("ConsumerBuffer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
- id(), buffer_state);
- return -EBUSY;
+ // Set the buffer state of this client to released if it is not already in
+ // released state.
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ client_state_mask())) {
+ return 0;
+ }
+ uint64_t updated_buffer_state = current_buffer_state & (~client_state_mask());
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to release the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to release the buffer and modify the buffer state to "
+ "%" PRIx64 ". About to try again.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ // The failure of compare_exchange_weak updates current_buffer_state.
+ updated_buffer_state = current_buffer_state & (~client_state_mask());
}
// On release, only the user requested metadata is copied back into the shared
@@ -141,8 +177,6 @@
if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
return error;
- // For release operation, the client don't need to change the state as it's
- // bufferhubd's job to flip the produer bit once all consumers are released.
return 0;
}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index 650da97..400def7 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -20,52 +20,104 @@
static constexpr uint32_t kMetadataUsage =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
-// Single producuer multiple (up to 63) consumers ownership signal.
+// Single buffer clients (up to 32) ownership signal.
// 64-bit atomic unsigned int.
+// Each client takes 2 bits. The first bit locates in the first 32 bits of
+// buffer_state; the second bit locates in the last 32 bits of buffer_state.
+// Client states:
+// Gained state 11. Exclusive write state.
+// Posted state 10.
+// Acquired state 01. Shared read state.
+// Released state 00.
//
-// MSB LSB
-// | |
-// v v
-// [C62|...|C1|C0|P]
-// Gain'ed state: [..|0|0|0] -> Exclusively Writable.
-// Post'ed state: [..|0|0|1]
-// Acquired'ed state: [..|X|X|1] -> At least one bit is set in higher 63 bits
-// Released'ed state: [..|X|X|0] -> At least one bit is set in higher 63 bits
-static constexpr int kMaxNumberOfClients = 64;
-static constexpr uint64_t kFirstClientBitMask = 1ULL;
-static constexpr uint64_t kConsumerStateMask = ~kFirstClientBitMask;
+// MSB LSB
+// | |
+// v v
+// [C31|...|C1|C0|C31| ... |C1|C0]
-static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state,
- uint64_t clear_mask, uint64_t set_mask) {
- uint64_t old_state;
- uint64_t new_state;
- do {
- old_state = buffer_state->load(std::memory_order_acquire);
- new_state = (old_state & ~clear_mask) | set_mask;
- } while (!buffer_state->compare_exchange_weak(old_state, new_state));
+// Maximum number of clients a buffer can have.
+static constexpr int kMaxNumberOfClients = 32;
+
+// Definition of bit masks.
+// MSB LSB
+// | kHighBitsMask | kLowbitsMask |
+// v v v
+// [b63| ... |b32|b31| ... |b0]
+
+// The location of lower 32 bits in the 64-bit buffer state.
+static constexpr uint64_t kLowbitsMask = (1ULL << kMaxNumberOfClients) - 1ULL;
+
+// The location of higher 32 bits in the 64-bit buffer state.
+static constexpr uint64_t kHighBitsMask = ~kLowbitsMask;
+
+// The client bit mask of the first client.
+static constexpr uint64_t kFirstClientBitMask =
+ (1ULL << kMaxNumberOfClients) + 1ULL;
+
+// Returns true if any of the client is in gained state.
+static inline bool AnyClientGained(uint64_t state) {
+ uint64_t high_bits = state >> kMaxNumberOfClients;
+ uint64_t low_bits = state & kLowbitsMask;
+ return high_bits == low_bits && low_bits != 0ULL;
}
-static inline bool IsBufferGained(uint64_t state) { return state == 0; }
-
-static inline bool IsBufferPosted(uint64_t state,
- uint64_t consumer_bit = kConsumerStateMask) {
- return (state & kFirstClientBitMask) && !(state & consumer_bit);
+// Returns true if the input client is in gained state.
+static inline bool IsClientGained(uint64_t state, uint64_t client_bit_mask) {
+ return state == client_bit_mask;
}
-static inline bool IsBufferAcquired(uint64_t state) {
- return (state & kFirstClientBitMask) && (state & kConsumerStateMask);
+// Returns true if any of the client is in posted state.
+static inline bool AnyClientPosted(uint64_t state) {
+ uint64_t high_bits = state >> kMaxNumberOfClients;
+ uint64_t low_bits = state & kLowbitsMask;
+ uint64_t posted_or_acquired = high_bits ^ low_bits;
+ return posted_or_acquired & high_bits;
}
-static inline bool IsBufferReleased(uint64_t state) {
- return !(state & kFirstClientBitMask) && (state & kConsumerStateMask);
+// Returns true if the input client is in posted state.
+static inline bool IsClientPosted(uint64_t state, uint64_t client_bit_mask) {
+ uint64_t client_bits = state & client_bit_mask;
+ if (client_bits == 0ULL)
+ return false;
+ uint64_t low_bits = client_bits & kLowbitsMask;
+ return low_bits == 0ULL;
}
-static inline uint64_t FindNextAvailableClientStateMask(uint64_t bits) {
- return ~bits - (~bits & (~bits - 1));
+// Return true if any of the client is in acquired state.
+static inline bool AnyClientAcquired(uint64_t state) {
+ uint64_t high_bits = state >> kMaxNumberOfClients;
+ uint64_t low_bits = state & kLowbitsMask;
+ uint64_t posted_or_acquired = high_bits ^ low_bits;
+ return posted_or_acquired & low_bits;
}
-static inline uint64_t FindFirstClearedBit() {
- return FindNextAvailableClientStateMask(kFirstClientBitMask);
+// Return true if the input client is in acquired state.
+static inline bool IsClientAcquired(uint64_t state, uint64_t client_bit_mask) {
+ uint64_t client_bits = state & client_bit_mask;
+ if (client_bits == 0ULL)
+ return false;
+ uint64_t high_bits = client_bits & kHighBitsMask;
+ return high_bits == 0ULL;
+}
+
+// Returns true if all clients are in released state.
+static inline bool IsBufferReleased(uint64_t state) { return state == 0ULL; }
+
+// Returns true if the input client is in released state.
+static inline bool IsClientReleased(uint64_t state, uint64_t client_bit_mask) {
+ return (state & client_bit_mask) == 0ULL;
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint64_t FindNextAvailableClientStateMask(uint64_t union_bits) {
+ uint64_t low_union = union_bits & kLowbitsMask;
+ if (low_union == kLowbitsMask)
+ return 0ULL;
+ uint64_t incremented = low_union + 1ULL;
+ uint64_t difference = incremented ^ low_union;
+ uint64_t new_low_bit = (difference + 1ULL) >> 1;
+ return new_low_bit + (new_low_bit << kMaxNumberOfClients);
}
struct __attribute__((packed, aligned(8))) MetadataHeader {
@@ -74,9 +126,21 @@
// part is subject for future updates, it's not stable cross Android version,
// so don't have it visible from outside of the Android platform (include Apps
// and vendor HAL).
+
+ // Every client takes up one bit from the higher 32 bits and one bit from the
+ // lower 32 bits in buffer_state.
std::atomic<uint64_t> buffer_state;
+
+ // Every client takes up one bit in fence_state. Only the lower 32 bits are
+ // valid. The upper 32 bits are there for easier manipulation, but the value
+ // should be ignored.
std::atomic<uint64_t> fence_state;
+
+ // Every client takes up one bit from the higher 32 bits and one bit from the
+ // lower 32 bits in active_clients_bit_mask.
std::atomic<uint64_t> active_clients_bit_mask;
+
+ // The index of the buffer queue where the buffer belongs to.
uint64_t queue_index;
// Public data format, which should be updated with caution. See more details
diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
index 7349779..7aa50b1 100644
--- a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -48,9 +48,8 @@
// Asynchronously acquires a bufer.
int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
- // This should be called after a successful Acquire call. If the fence is
- // valid the fence determines the buffer usage, otherwise the buffer is
- // released immediately.
+ // Releases the buffer from any buffer state. If the fence is valid the fence
+ // determines the buffer usage, otherwise the buffer is released immediately.
// This returns zero or a negative unix error code.
int Release(const LocalHandle& release_fence);
int ReleaseAsync();
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
index f36e169..c177970 100644
--- a/libs/vr/libbufferhub/producer_buffer.cpp
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -79,14 +79,43 @@
if (const int error = CheckMetadata(meta->user_metadata_size))
return error;
- // Check invalid state transition.
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
- ALOGE("ProducerBuffer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
- id(), buffer_state);
+ // The buffer can be posted iff the buffer state for this client is gained.
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::IsClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGE("%s: not gained, id=%d state=%" PRIx64 ".", __FUNCTION__, id(),
+ current_buffer_state);
return -EBUSY;
}
+ // Set the producer client buffer state to released, other clients' buffer
+ // state to posted.
+ uint64_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ uint64_t updated_buffer_state = current_active_clients_bit_mask &
+ (~client_state_mask()) &
+ BufferHubDefs::kHighBitsMask;
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to post the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to post the buffer and modify the buffer state to "
+ "%" PRIx64
+ ". About to try again if the buffer is still gained by this client.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (!BufferHubDefs::IsClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to post the buffer. The buffer is no longer gained, "
+ "id=%d state=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state);
+ return -EBUSY;
+ }
+ }
+
// Copy the canonical metadata.
void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
@@ -101,10 +130,6 @@
if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
return error;
- // Set the producer bit atomically to transit into posted state.
- // The producer state bit mask is kFirstClientBitMask for now.
- BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
- BufferHubDefs::kFirstClientBitMask);
return 0;
}
@@ -136,25 +161,52 @@
int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta,
LocalHandle* out_fence, bool gain_posted_buffer) {
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- ALOGD_IF(TRACE, "ProducerBuffer::LocalGain: buffer=%d, state=%" PRIx64 ".",
- id(), buffer_state);
-
if (!out_meta)
return -EINVAL;
- if (BufferHubDefs::IsBufferGained(buffer_state)) {
- // We don't want to log error when gaining a newly allocated
- // buffer.
- ALOGI("ProducerBuffer::LocalGain: already gained id=%d.", id());
- return -EALREADY;
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx64 ".", __FUNCTION__, id(),
+ current_buffer_state);
+
+ if (BufferHubDefs::IsClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGI("%s: already gained id=%d.", __FUNCTION__, id());
+ return 0;
}
- if (BufferHubDefs::IsBufferAcquired(buffer_state) ||
- (BufferHubDefs::IsBufferPosted(buffer_state) && !gain_posted_buffer)) {
- ALOGE("ProducerBuffer::LocalGain: not released id=%d state=%" PRIx64 ".",
- id(), buffer_state);
+ if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::AnyClientPosted(current_buffer_state) &&
+ !gain_posted_buffer)) {
+ ALOGE("%s: not released id=%d state=%" PRIx64 ".", __FUNCTION__, id(),
+ current_buffer_state);
return -EBUSY;
}
+ // Change the buffer state to gained state.
+ uint64_t updated_buffer_state = client_state_mask();
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to gain the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to gain the buffer and modify the buffer state to "
+ "%" PRIx64
+ ". About to try again if the buffer is still not read by other "
+ "clients.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+
+ if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::AnyClientPosted(current_buffer_state) &&
+ !gain_posted_buffer)) {
+ ALOGE(
+ "%s: Failed to gain the buffer. The buffer is no longer released. "
+ "id=%d state=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state);
+ return -EBUSY;
+ }
+ }
// Canonical metadata is undefined on Gain. Except for user_metadata and
// release_fence_mask. Fill in the user_metadata_ptr in address space of the
@@ -169,16 +221,20 @@
out_meta->user_metadata_ptr = 0;
}
- uint64_t fence_state = fence_state_->load(std::memory_order_acquire);
+ uint64_t current_fence_state = fence_state_->load(std::memory_order_acquire);
+ uint64_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
// If there is an release fence from consumer, we need to return it.
- if (fence_state & BufferHubDefs::kConsumerStateMask) {
+ // TODO(b/112007999) add an atomic variable in metadata header in shared
+ // memory to indicate which client is the last producer of the buffer.
+ // Currently, assume the first client is the only producer to the buffer.
+ if (current_fence_state & current_active_clients_bit_mask &
+ (~BufferHubDefs::kFirstClientBitMask)) {
*out_fence = shared_release_fence_.Duplicate();
out_meta->release_fence_mask =
- fence_state & BufferHubDefs::kConsumerStateMask;
+ current_fence_state & current_active_clients_bit_mask;
}
- // Clear out all bits and the buffer is now back to gained state.
- buffer_state_->store(0ULL);
return 0;
}
@@ -232,7 +288,8 @@
// TODO(b/112338294) Keep here for reference. Remove it after new logic is
// written.
/* uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (!BufferHubDefs::IsClientGained(
+ buffer_state, BufferHubDefs::kFirstClientStateMask)) {
// Can only detach a ProducerBuffer when it's in gained state.
ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx64
") is not in gained state.",
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index f7942d0..9c4f73f 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -532,7 +532,8 @@
Status<size_t> ProducerQueue::InsertBuffer(
const std::shared_ptr<BufferProducer>& buffer) {
if (buffer == nullptr ||
- !BufferHubDefs::IsBufferGained(buffer->buffer_state())) {
+ !BufferHubDefs::IsClientGained(buffer->buffer_state(),
+ buffer->client_state_mask())) {
ALOGE(
"ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
"gained state.");
@@ -637,7 +638,7 @@
static_cast<int>(*slot));
return ErrorStatus(EIO);
}
- if (!BufferHubDefs::IsBufferAcquired(buffer->buffer_state())) {
+ if (!BufferHubDefs::AnyClientAcquired(buffer->buffer_state())) {
*slot = *iter;
unavailable_buffers_slot_.erase(iter);
unavailable_buffers_slot_.push_back(*slot);
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 874eb3a..fd6ca43 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -112,7 +112,7 @@
// Consumer acquires a buffer.
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
+ EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
EXPECT_EQ(mi.index, i);
@@ -334,6 +334,7 @@
std::shared_ptr<BufferProducer> p1 = BufferProducer::Create(
kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
ASSERT_TRUE(p1 != nullptr);
+ ASSERT_EQ(p1->GainAsync(), 0);
// Inserting a posted buffer will fail.
DvrNativeBufferMetadata meta;
@@ -345,9 +346,10 @@
// Inserting a gained buffer will succeed.
std::shared_ptr<BufferProducer> p2 = BufferProducer::Create(
kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
+ ASSERT_EQ(p2->GainAsync(), 0);
ASSERT_TRUE(p2 != nullptr);
status_or_slot = producer_queue_->InsertBuffer(p2);
- EXPECT_TRUE(status_or_slot.ok());
+ EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
// This is the first buffer inserted, should take slot 0.
size_t slot = status_or_slot.get();
EXPECT_EQ(slot, 0);
@@ -585,7 +587,7 @@
mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
+ EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
@@ -689,7 +691,7 @@
size_t cs1, cs2;
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
- ASSERT_TRUE(c1_status.ok());
+ ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
ASSERT_EQ(consumer_queue_->count(), 0U);
@@ -905,7 +907,7 @@
ASSERT_NE(producer_buffer, nullptr);
ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok());
+ ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
}
status = producer_queue_->FreeAllBuffers();
@@ -999,7 +1001,7 @@
// Make sure the buffer can be dequeued from consumer side.
auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s4.ok());
+ EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
EXPECT_EQ(consumer_queue_->capacity(), 1U);
auto consumer = s4.take();
@@ -1066,7 +1068,7 @@
// Make sure the buffer can be dequeued from consumer side.
auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s3.ok());
+ EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
EXPECT_EQ(consumer_queue_->capacity(), 1U);
auto consumer = s3.take();
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 07904fb..4f8bdbf 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -85,9 +85,6 @@
"-Wno-error=sign-compare", // to fix later
"-Wno-unused-variable",
],
- cppflags: [
- "-std=c++1z"
- ],
shared_libs: sharedLibraries,
whole_static_libs: staticLibraries,
header_libs: headerLibraries,
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index 1a38dd8..6f97f0d 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -51,10 +51,44 @@
return Void();
}
-Return<void> BufferHubService::importBuffer(const hidl_handle& /*nativeHandle*/,
+Return<void> BufferHubService::importBuffer(const hidl_handle& tokenHandle,
importBuffer_cb _hidl_cb) {
- // TODO(b/118614157): implement buffer import
- _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::NO_ERROR);
+ if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts != 1) {
+ // nullptr handle or wrong format
+ _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+ return Void();
+ }
+
+ uint32_t token = tokenHandle->data[0];
+
+ wp<BufferClient> originClientWp;
+ {
+ std::lock_guard<std::mutex> lock(mTokenMapMutex);
+ auto iter = mTokenMap.find(token);
+ if (iter == mTokenMap.end()) {
+ // Invalid token
+ _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+ return Void();
+ }
+
+ originClientWp = iter->second;
+ mTokenMap.erase(iter);
+ }
+
+ // Check if original client is dead
+ sp<BufferClient> originClient = originClientWp.promote();
+ if (!originClient) {
+ // Should not happen since token should be removed if already gone
+ ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
+ _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::BUFFER_FREED);
+ return Void();
+ }
+
+ sp<BufferClient> client = new BufferClient(*originClient);
+
+ std::lock_guard<std::mutex> lock(mClientListMutex);
+ mClientList.push_back(client);
+ _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
return Void();
}
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 5456ec3..769ec86 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -37,15 +37,19 @@
class BufferClient : public IBufferClient {
public:
// Creates a server-side buffer client from an existing BufferNode. Note that
- // this funciton takes ownership of the shared_ptr.
+ // this function takes ownership of the shared_ptr.
// Returns a raw pointer to the BufferClient on success, nullptr on failure.
static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
+ // Creates a BufferClient from an existing BufferClient. Will share the same BufferNode.
+ explicit BufferClient(const BufferClient& other)
+ : mService(other.mService), mBufferNode(other.mBufferNode) {}
+
Return<void> duplicate(duplicate_cb _hidl_cb) override;
private:
BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
- : mService(service), mBufferNode(node){};
+ : mService(service), mBufferNode(node) {}
wp<BufferHubService> mService;
std::shared_ptr<BufferNode> mBufferNode;
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index e3f657f..6535659 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -42,7 +42,7 @@
Return<void> allocateBuffer(const HardwareBufferDescription& description,
const uint32_t userMetadataSize,
allocateBuffer_cb _hidl_cb) override;
- Return<void> importBuffer(const hidl_handle& nativeHandle, importBuffer_cb _hidl_cb) override;
+ Return<void> importBuffer(const hidl_handle& tokenHandle, importBuffer_cb _hidl_cb) override;
// Non-binder functions
// Internal help function for IBufferClient::duplicate.
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 250bbee..47bed65 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,7 +21,6 @@
"-Wunused",
"-Wunreachable-code",
],
- cppflags: ["-std=c++1z"],
srcs: [
":gpuservice_sources",
],
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index bea4f91..1dca1b6 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -3093,7 +3093,7 @@
continue;
}
- if (windowHandle->getInfo()->hasFocus) {
+ if (windowHandle->getInfo()->hasFocus && windowHandle->getInfo()->visible) {
newFocusedWindowHandle = windowHandle;
}
if (windowHandle == mLastHoverWindowHandle) {
@@ -3518,7 +3518,7 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=%f,%f"
+ "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
toString(windowInfo->paused),
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index db945bb..24b0dd7 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -117,6 +117,8 @@
void onUidGone(uid_t uid, bool disabled);
void onUidActive(uid_t uid);
void onUidIdle(uid_t uid, bool disabled);
+ void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
+ int64_t procStateSeq __unused) {}
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index fb2cb0f..22e4d1e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -9,7 +9,6 @@
"-Wunused",
"-Wunreachable-code",
],
- cppflags: ["-std=c++1z"],
}
cc_defaults {
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index fd40f6c..9b1c0db 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -24,6 +24,8 @@
#include "DisplayDevice.h"
#include "LayerRejecter.h"
+#include "TimeStats/TimeStats.h"
+
#include <renderengine/RenderEngine.h>
#include <gui/BufferItem.h>
@@ -71,7 +73,7 @@
destroyAllHwcLayersPlusChildren();
}
- mTimeStats.onDestroy(getSequence());
+ mFlinger->mTimeStats->onDestroy(getSequence());
}
void BufferLayer::useSurfaceDamage() {
@@ -334,7 +336,7 @@
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
const int32_t layerID = getSequence();
- mTimeStats.setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+ mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
if (frameReadyFence->isValid()) {
@@ -346,13 +348,13 @@
}
if (presentFence->isValid()) {
- mTimeStats.setPresentFence(layerID, mCurrentFrameNumber, presentFence);
+ mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
- mTimeStats.setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
+ mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
mFrameTracker.setActualPresentTime(actualPresentTime);
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 162f391..b784d11 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,6 +17,8 @@
#include "BufferQueueLayer.h"
#include "LayerRejecter.h"
+#include "TimeStats/TimeStats.h"
+
#include <system/window.h>
namespace android {
@@ -246,7 +248,7 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
- mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
@@ -260,7 +262,7 @@
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.clear();
mQueuedFrames = 0;
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
}
// Once we have hit this state, the shadow queue may no longer
@@ -281,13 +283,14 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
- mTimeStats.setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime);
- mTimeStats.setLatchTime(layerID, currentFrameNumber, latchTime);
+ mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
+ mQueueItems[0].mFenceTime);
+ mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
mQueueItems.removeAt(0);
}
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5ffd8ad..1f71a44 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -21,6 +21,8 @@
#include "BufferStateLayer.h"
+#include "TimeStats/TimeStats.h"
+
#include <private/gui/SyncFeatures.h>
#include <renderengine/Image.h>
@@ -457,7 +459,7 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mTimeStats.removeTimeRecord(layerID, getFrameNumber());
+ mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
return BAD_VALUE;
}
@@ -469,7 +471,7 @@
if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
// TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
if (!releaseFence->isValid()) {
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return UNKNOWN_ERROR;
}
@@ -479,7 +481,7 @@
auto currentStatus = s.acquireFence->getStatus();
if (currentStatus == Fence::Status::Invalid) {
ALOGE("Existing fence has invalid state");
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
@@ -487,7 +489,7 @@
if (incomingStatus == Fence::Status::Invalid) {
ALOGE("New fence has invalid state");
mDrawingState.acquireFence = releaseFence;
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
@@ -503,7 +505,7 @@
// synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union
mDrawingState.acquireFence = releaseFence;
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
mDrawingState.acquireFence = mergedFence;
@@ -526,15 +528,15 @@
// a GL-composited layer) not at all.
status_t err = bindTextureImage();
if (err != NO_ERROR) {
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
}
// TODO(marissaw): properly support mTimeStats
- mTimeStats.setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
- mTimeStats.setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
- mTimeStats.setLatchTime(layerID, getFrameNumber(), latchTime);
+ mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
+ mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+ mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7b681ea..15e416a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -52,6 +52,7 @@
#include "SurfaceFlinger.h"
#include "DisplayHardware/HWComposer.h"
+#include "TimeStats/TimeStats.h"
#include <renderengine/RenderEngine.h>
@@ -1522,14 +1523,14 @@
void Layer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.onDisconnect();
- mTimeStats.onDisconnect(getSequence());
+ mFlinger->mTimeStats->onDestroy(getSequence());
}
void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
- mTimeStats.setPostTime(getSequence(), newTimestamps->frameNumber, getName().c_str(),
- newTimestamps->postedTime);
+ mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
+ getName().c_str(), newTimestamps->postedTime);
}
Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -2126,10 +2127,13 @@
info.frameBottom = screenBounds.bottom - info.surfaceInset;
ui::Transform t = getTransform();
- info.windowXScale *= 1.0f / t.sx();
- info.windowYScale *= 1.0f / t.sy();
-
- info.touchableRegion.scaleSelf(t.sx(), t.sy());
+ const float xScale = t.sx();
+ const float yScale = t.sy();
+ if (xScale != 1.0f || yScale != 1.0f) {
+ info.windowXScale *= 1.0f / xScale;
+ info.windowYScale *= 1.0f / yScale;
+ info.touchableRegion.scaleSelf(xScale, yScale);
+ }
info.touchableRegion = info.touchableRegion.translate(
screenBounds.left,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 270c15a..6ea80c7 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -48,7 +48,6 @@
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
-#include "TimeStats/TimeStats.h"
#include "TransactionCompletedThread.h"
#include "DisplayHardware/HWComposer.h"
@@ -789,8 +788,6 @@
FenceTimeline mAcquireTimeline;
FenceTimeline mReleaseTimeline;
- TimeStats& mTimeStats = TimeStats::getInstance();
-
// main thread
sp<GraphicBuffer> mActiveBuffer;
ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5b3c477..a8de576 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -95,6 +95,7 @@
#include "Scheduler/InjectVSyncSource.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
#include <cutils/compiler.h>
@@ -263,6 +264,7 @@
mDebugInTransaction(0),
mLastTransactionTime(0),
mForceFullDamage(false),
+ mTimeStats(factory.createTimeStats()),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
mRefreshStartTime(0),
@@ -1526,7 +1528,7 @@
mFrameMissedCount += frameMissed;
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
if (frameMissed) {
- mTimeStats.incrementMissedFrames();
+ mTimeStats->incrementMissedFrames();
if (mPropagateBackpressure) {
signalLayerUpdate();
break;
@@ -1951,12 +1953,12 @@
mAnimFrameTracker.advanceFrame();
}
- mTimeStats.incrementTotalFrames();
+ mTimeStats->incrementTotalFrames();
if (mHadClientComposition) {
- mTimeStats.incrementClientCompositionFrames();
+ mTimeStats->incrementClientCompositionFrames();
}
- mTimeStats.setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(presentFenceTime);
if (display && getHwComposer().isConnected(*display->getId()) && !display->isPoweredOn()) {
return;
@@ -4056,7 +4058,7 @@
}
if (display->isPrimary()) {
- mTimeStats.setPowerMode(mode);
+ mTimeStats->setPowerMode(mode);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
@@ -4199,7 +4201,7 @@
if ((index < numArgs) && (args[index] == String16("--timestats"))) {
index++;
- mTimeStats.parseArgs(asProto, args, index, result);
+ mTimeStats->parseArgs(asProto, args, index, result);
dumpAll = false;
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index eff5fca..9039a14 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -72,7 +72,6 @@
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VSyncModulator.h"
-#include "TimeStats/TimeStats.h"
#include <map>
#include <mutex>
@@ -103,6 +102,7 @@
class Layer;
class Surface;
class SurfaceFlingerBE;
+class TimeStats;
class VSyncSource;
struct CompositionInfo;
@@ -926,7 +926,7 @@
std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
SurfaceTracing mTracing;
LayerStats mLayerStats;
- TimeStats& mTimeStats = TimeStats::getInstance();
+ std::unique_ptr<TimeStats> mTimeStats;
bool mUseHwcVirtualDisplays = false;
std::atomic<uint32_t> mFrameMissedCount{0};
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 1261aee..88649e3 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -33,6 +33,7 @@
#include "Scheduler/EventControlThread.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
namespace android::surfaceflinger {
@@ -116,6 +117,10 @@
sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
return new ColorLayer(args);
}
+
+ std::unique_ptr<TimeStats> createTimeStats() override {
+ return std::make_unique<TimeStats>();
+ }
};
static Factory factory;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 496bbf0..1c27cc7 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -44,6 +44,7 @@
class StartPropertySetThread;
class SurfaceFlinger;
class SurfaceInterceptor;
+class TimeStats;
struct DisplayDeviceCreationArgs;
struct LayerCreationArgs;
@@ -82,6 +83,8 @@
virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+ virtual std::unique_ptr<TimeStats> createTimeStats() = 0;
+
protected:
~Factory() = default;
};
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 9730e8c..2b9f5c8 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,11 +32,6 @@
namespace android {
-TimeStats& TimeStats::getInstance() {
- static TimeStats sInstance;
- return sInstance;
-}
-
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
String8& result) {
ATRACE_CALL();
@@ -268,11 +263,9 @@
if (!mTimeStatsTracker.count(layerID)) return;
LayerRecord& layerRecord = mTimeStatsTracker[layerID];
if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
- ALOGE("[%d]-[%s]-timeRecords is already at its maximum size[%zu]. Please file a bug.",
+ ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
- layerRecord.timeRecords.clear();
- layerRecord.prevTimeRecord.ready = false;
- layerRecord.waitData = -1;
+ mTimeStatsTracker.erase(layerID);
return;
}
// For most media content, the acquireFence is invalid because the buffer is
@@ -283,7 +276,9 @@
{
.frameNumber = frameNumber,
.postTime = postTime,
+ .latchTime = postTime,
.acquireTime = postTime,
+ .desiredTime = postTime,
},
};
layerRecord.timeRecords.push_back(timeRecord);
@@ -394,18 +389,6 @@
flushAvailableRecordsToStatsLocked(layerID);
}
-void TimeStats::onDisconnect(int32_t layerID) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
- ALOGV("[%d]-onDisconnect", layerID);
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- flushAvailableRecordsToStatsLocked(layerID);
- mTimeStatsTracker.erase(layerID);
-}
-
void TimeStats::onDestroy(int32_t layerID) {
if (!mEnabled.load()) return;
@@ -414,25 +397,9 @@
std::lock_guard<std::mutex> lock(mMutex);
if (!mTimeStatsTracker.count(layerID)) return;
- flushAvailableRecordsToStatsLocked(layerID);
mTimeStatsTracker.erase(layerID);
}
-void TimeStats::clearLayerRecord(int32_t layerID) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
- ALOGV("[%d]-clearLayerRecord", layerID);
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
- layerRecord.timeRecords.clear();
- layerRecord.prevTimeRecord.ready = false;
- layerRecord.waitData = -1;
- layerRecord.droppedFrames = 0;
-}
-
void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
if (!mEnabled.load()) return;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 184bf40..0b24c46 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -38,10 +38,6 @@
class String8;
class TimeStats {
- // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
- // static const size_t MAX_NUM_LAYER_RECORDS = 200;
- static const size_t MAX_NUM_TIME_RECORDS = 64;
-
struct FrameTime {
uint64_t frameNumber = 0;
nsecs_t postTime = 0;
@@ -80,8 +76,12 @@
};
public:
- static TimeStats& getInstance();
+ TimeStats() = default;
+ ~TimeStats() = default;
+
void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
+ bool isEnabled();
+
void incrementTotalFrames();
void incrementMissedFrames();
void incrementClientCompositionFrames();
@@ -96,21 +96,19 @@
void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
void setPresentFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence);
- // On producer disconnect with BufferQueue.
- void onDisconnect(int32_t layerID);
- // On layer tear down.
+ // Clean up the layer record
void onDestroy(int32_t layerID);
- // When SF is cleaning up the queue, clear the LayerRecord as well.
- void clearLayerRecord(int32_t layerID);
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
void setPowerMode(int32_t powerMode);
void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
-private:
- TimeStats() = default;
+ // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
+ // static const size_t MAX_NUM_LAYER_RECORDS = 200;
+ static const size_t MAX_NUM_TIME_RECORDS = 64;
+private:
bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerID);
void flushPowerTimeLocked();
@@ -119,7 +117,6 @@
void enable();
void disable();
void clear();
- bool isEnabled();
void dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result);
std::atomic<bool> mEnabled = false;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index bef6b7c..b937f41 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -17,7 +17,6 @@
},
cppflags: [
- "-std=c++1z",
"-Werror",
"-Wno-c++98-compat-pedantic",
"-Wno-disabled-macro-expansion",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 28f3ef2..42b7146 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -32,6 +32,7 @@
"LayerHistoryTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplaySurface.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 85c835e..6d4f7ef 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -28,6 +28,8 @@
#include "SurfaceFlingerFactory.h"
#include "SurfaceInterceptor.h"
+#include "TimeStats/TimeStats.h"
+
namespace android {
class EventThread;
@@ -131,6 +133,11 @@
return nullptr;
}
+ std::unique_ptr<TimeStats> createTimeStats() override {
+ // TODO: Use test-fixture controlled factory
+ return std::make_unique<TimeStats>();
+ }
+
using CreateBufferQueueFunction =
std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
sp<IGraphicBufferConsumer>* /* outConsumer */,
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
new file mode 100644
index 0000000..186ed79
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <random>
+
+#include "TimeStats/TimeStats.h"
+
+using namespace android::surfaceflinger;
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+// clang-format off
+#define FMT_PROTO true
+#define FMT_STRING false
+#define LAYER_ID_0 0
+#define LAYER_ID_1 1
+#define LAYER_ID_INVALID -1
+#define NUM_LAYERS 1
+#define NUM_LAYERS_INVALID "INVALID"
+
+enum InputCommand : int32_t {
+ ENABLE = 0,
+ DISABLE = 1,
+ CLEAR = 2,
+ DUMP_ALL = 3,
+ DUMP_MAXLAYERS_1 = 4,
+ DUMP_MAXLAYERS_INVALID = 5,
+ INPUT_COMMAND_BEGIN = ENABLE,
+ INPUT_COMMAND_END = DUMP_MAXLAYERS_INVALID,
+ INPUT_COMMAND_RANGE = INPUT_COMMAND_END - INPUT_COMMAND_BEGIN + 1,
+};
+
+enum TimeStamp : int32_t {
+ POST = 0,
+ ACQUIRE = 1,
+ ACQUIRE_FENCE = 2,
+ LATCH = 3,
+ DESIRED = 4,
+ PRESENT = 5,
+ PRESENT_FENCE = 6,
+ TIME_STAMP_BEGIN = POST,
+ TIME_STAMP_END = PRESENT,
+ TIME_STAMP_RANGE = TIME_STAMP_END - TIME_STAMP_BEGIN + 1,
+};
+
+static const TimeStamp NORMAL_SEQUENCE[] = {
+ TimeStamp::POST,
+ TimeStamp::ACQUIRE,
+ TimeStamp::LATCH,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT,
+};
+
+static const TimeStamp NORMAL_SEQUENCE_2[] = {
+ TimeStamp::POST,
+ TimeStamp::ACQUIRE_FENCE,
+ TimeStamp::LATCH,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT_FENCE,
+};
+
+static const TimeStamp UNORDERED_SEQUENCE[] = {
+ TimeStamp::ACQUIRE,
+ TimeStamp::LATCH,
+ TimeStamp::POST,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT,
+};
+
+static const TimeStamp INCOMPLETE_SEQUENCE[] = {
+ TimeStamp::POST,
+};
+// clang-format on
+
+class TimeStatsTest : public testing::Test {
+public:
+ TimeStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~TimeStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ std::string inputCommand(InputCommand cmd, bool useProto);
+
+ void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+
+ int32_t genRandomInt32(int32_t begin, int32_t end);
+
+ template <size_t N>
+ void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
+ nsecs_t ts) {
+ for (size_t i = 0; i < N; i++, ts += 1000000) {
+ setTimeStamp(sequence[i], id, frameNumber, ts);
+ }
+ }
+
+ std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
+ std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>();
+};
+
+std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
+ size_t index = 0;
+ String8 result;
+ Vector<String16> args;
+
+ switch (cmd) {
+ case InputCommand::ENABLE:
+ args.push_back(String16("-enable"));
+ break;
+ case InputCommand::DISABLE:
+ args.push_back(String16("-disable"));
+ break;
+ case InputCommand::CLEAR:
+ args.push_back(String16("-clear"));
+ break;
+ case InputCommand::DUMP_ALL:
+ args.push_back(String16("-dump"));
+ break;
+ case InputCommand::DUMP_MAXLAYERS_1:
+ args.push_back(String16("-dump"));
+ args.push_back(String16("-maxlayers"));
+ args.push_back(String16(std::to_string(NUM_LAYERS).c_str()));
+ break;
+ case InputCommand::DUMP_MAXLAYERS_INVALID:
+ args.push_back(String16("-dump"));
+ args.push_back(String16("-maxlayers"));
+ args.push_back(String16(NUM_LAYERS_INVALID));
+ break;
+ default:
+ ALOGD("Invalid control command");
+ }
+
+ EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result));
+ return std::string(result.string(), result.size());
+}
+
+static std::string genLayerName(int32_t layerID) {
+ return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+}
+
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+ switch (type) {
+ case TimeStamp::POST:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+ break;
+ case TimeStamp::ACQUIRE:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::ACQUIRE_FENCE:
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setAcquireFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ break;
+ case TimeStamp::LATCH:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setLatchTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::DESIRED:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::PRESENT:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::PRESENT_FENCE:
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ break;
+ default:
+ ALOGD("Invalid timestamp type");
+ }
+}
+
+int32_t TimeStatsTest::genRandomInt32(int32_t begin, int32_t end) {
+ std::uniform_int_distribution<int32_t> distr(begin, end);
+ return distr(mRandomEngine);
+}
+
+TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ ASSERT_TRUE(mTimeStats->isEnabled());
+
+ EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
+ ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, canIncreaseGlobalStats) {
+ constexpr size_t TOTAL_FRAMES = 5;
+ constexpr size_t MISSED_FRAMES = 4;
+ constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+ }
+ for (size_t i = 0; i < MISSED_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+ }
+ for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+ }
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_TRUE(globalProto.has_total_frames());
+ EXPECT_EQ(TOTAL_FRAMES, globalProto.total_frames());
+ ASSERT_TRUE(globalProto.has_missed_frames());
+ EXPECT_EQ(MISSED_FRAMES, globalProto.missed_frames());
+ ASSERT_TRUE(globalProto.has_client_composition_frames());
+ EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)));
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_OFF));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(6000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(8000000)));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.present_to_present_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.present_to_present().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ EXPECT_EQ(2, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+ ASSERT_EQ(6, layerProto.deltas_size());
+ for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+ ASSERT_EQ(1, deltaProto.histograms_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ if ("post2acquire" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("post2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(4, histogramProto.time_millis());
+ } else if ("acquire2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(3, histogramProto.time_millis());
+ } else if ("latch2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("desired2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("present2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else {
+ FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+ }
+ }
+}
+
+TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_INVALID, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_INVALID, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertMultipleLayersTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(2, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertUnorderedLayerTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(UNORDERED_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+ ASSERT_EQ(6, layerProto.deltas_size());
+ for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+ ASSERT_EQ(1, deltaProto.histograms_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ if ("post2acquire" == deltaProto.delta_name()) {
+ EXPECT_EQ(0, histogramProto.time_millis());
+ } else if ("post2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("acquire2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("latch2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("desired2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("present2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else {
+ FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+ }
+ }
+}
+
+TEST_F(TimeStatsTest, canRemoveTimeRecord) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(0, 2));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canRecoverFromIncompleteTimeRecordError) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint64_t frameNumber = 1;
+ nsecs_t ts = 1000000;
+ insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+ frameNumber++;
+ ts += 1000000;
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
+ }
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, layerTimeStatsOnDestroy) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(0));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canClearTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.total_frames());
+ EXPECT_EQ(0, globalProto.missed_frames());
+ EXPECT_EQ(0, globalProto.client_composition_frames());
+ EXPECT_EQ(0, globalProto.present_to_present_size());
+ EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 3, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(
+ globalProto.ParseFromString(inputCommand(InputCommand::DUMP_MAXLAYERS_1, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_1), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(2, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(
+ inputCommand(InputCommand::DUMP_MAXLAYERS_INVALID, FMT_PROTO)));
+
+ ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canSurviveMonkey) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < 10000000; ++i) {
+ const int32_t layerID = genRandomInt32(-1, 10);
+ const int32_t frameNumber = genRandomInt32(1, 10);
+ switch (genRandomInt32(0, 100)) {
+ case 0:
+ ALOGV("removeTimeRecord");
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+ continue;
+ case 1:
+ ALOGV("onDestroy");
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+ continue;
+ }
+ TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
+ const int32_t ts = genRandomInt32(1, 1000000000);
+ ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
+ setTimeStamp(type, layerID, frameNumber, ts);
+ }
+}
+
+} // namespace
+} // namespace android
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index b5e6bb4..0a59f29 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -75,5 +75,3 @@
name: "bufferhubd",
init_rc: ["bufferhubd.rc"],
}
-
-subdirs = ["tests"]
\ No newline at end of file
diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp
index 6c64bcd..cf072b6 100644
--- a/services/vr/bufferhubd/buffer_channel.cpp
+++ b/services/vr/bufferhubd/buffer_channel.cpp
@@ -52,8 +52,7 @@
buffer_id(), /*consumer_count=*/0, buffer_node_->buffer_desc().width,
buffer_node_->buffer_desc().height, buffer_node_->buffer_desc().layers,
buffer_node_->buffer_desc().format, buffer_node_->buffer_desc().usage,
- /*pending_count=*/0, /*state=*/0, /*signaled_mask=*/0,
- /*index=*/0);
+ /*state=*/0, /*signaled_mask=*/0, /*index=*/0);
}
void BufferChannel::HandleImpulse(Message& /*message*/) {
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index b73c47d..f50d292 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -56,8 +56,6 @@
stream << " ";
stream << std::setw(10) << "Usage";
stream << " ";
- stream << std::setw(9) << "Pending";
- stream << " ";
stream << std::setw(18) << "State";
stream << " ";
stream << std::setw(18) << "Signaled";
@@ -90,8 +88,6 @@
stream << std::setw(8) << info.usage;
stream << std::dec << std::setfill(' ');
stream << " ";
- stream << std::setw(9) << info.pending_count;
- stream << " ";
stream << "0x" << std::hex << std::setfill('0');
stream << std::setw(16) << info.state;
stream << " ";
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index 98ef917..f936e95 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -153,11 +153,10 @@
}
}
-bool ConsumerChannel::OnProducerPosted() {
+void ConsumerChannel::OnProducerPosted() {
acquired_ = false;
released_ = false;
SignalAvailable();
- return true;
}
void ConsumerChannel::OnProducerClosed() {
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
index e47ffa3..676617d 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
@@ -52,7 +52,6 @@
uint32_t layer_count = 0;
uint32_t format = 0;
uint64_t usage = 0;
- size_t pending_count = 0;
uint64_t state = 0;
uint64_t signaled_mask = 0;
uint64_t index = 0;
@@ -63,8 +62,7 @@
BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format, uint64_t usage,
- size_t pending_count, uint64_t state, uint64_t signaled_mask,
- uint64_t index)
+ uint64_t state, uint64_t signaled_mask, uint64_t index)
: id(id),
type(kProducerType),
consumer_count(consumer_count),
@@ -73,7 +71,6 @@
layer_count(layer_count),
format(format),
usage(usage),
- pending_count(pending_count),
state(state),
signaled_mask(signaled_mask),
index(index) {}
diff --git a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
index 3298529..de0f23c 100644
--- a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
@@ -26,7 +26,7 @@
uint64_t client_state_mask() const { return client_state_mask_; }
BufferInfo GetBufferInfo() const override;
- bool OnProducerPosted();
+ void OnProducerPosted();
void OnProducerClosed();
private:
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 3ad9c70..4734439 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -62,8 +62,7 @@
pdx::Status<void> OnConsumerRelease(Message& message,
LocalFence release_fence);
- void DecrementPendingConsumers();
- void OnConsumerOrphaned(ConsumerChannel* channel);
+ void OnConsumerOrphaned(const uint64_t& consumer_state_mask);
void AddConsumer(ConsumerChannel* channel);
void RemoveConsumer(ConsumerChannel* channel);
@@ -74,9 +73,6 @@
private:
std::vector<ConsumerChannel*> consumer_channels_;
- // This counts the number of consumers left to process this buffer. If this is
- // zero then the producer can re-acquire ownership.
- int pending_consumers_;
IonBuffer buffer_;
@@ -91,7 +87,6 @@
// highest bit is reserved for the producer and should not be set.
uint64_t orphaned_consumer_bit_mask_{0ULL};
- bool producer_owns_;
LocalFence post_fence_;
LocalFence returned_fence_;
size_t user_metadata_size_; // size of user requested buffer buffer size.
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index c6e8ea9..ab1d94c 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -56,8 +56,6 @@
uint64_t usage, size_t user_metadata_size,
int* error)
: BufferHubChannel(service, channel_id, channel_id, kProducerType),
- pending_consumers_(0),
- producer_owns_(true),
user_metadata_size_(user_metadata_size),
metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
user_metadata_size) {
@@ -184,7 +182,7 @@
return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
buffer_.height(), buffer_.layer_count(), buffer_.format(),
- buffer_.usage(), pending_consumers_,
+ buffer_.usage(),
buffer_state_->load(std::memory_order_acquire),
signaled_mask, metadata_header_->queue_index);
}
@@ -255,16 +253,16 @@
// that release active_clients_bit_mask_ need to be visible here.
uint64_t current_active_clients_bit_mask =
active_clients_bit_mask_->load(std::memory_order_acquire);
- uint64_t client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
- current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
- if (client_state_mask == 0ULL) {
- ALOGE(
- "ProducerChannel::CreateConsumer: reached the maximum mumber of "
- "consumers per producer: 63.");
+ uint64_t consumer_state_mask =
+ BufferHubDefs::FindNextAvailableClientStateMask(
+ current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+ if (consumer_state_mask == 0ULL) {
+ ALOGE("%s: reached the maximum mumber of consumers per producer: 63.",
+ __FUNCTION__);
return ErrorStatus(E2BIG);
}
uint64_t updated_active_clients_bit_mask =
- current_active_clients_bit_mask | client_state_mask;
+ current_active_clients_bit_mask | consumer_state_mask;
// Set the updated value only if the current value stays the same as what was
// read before. If the comparison succeeds, update the value without
// reordering anything before or after this read-modify-write in the current
@@ -276,24 +274,24 @@
while (!active_clients_bit_mask_->compare_exchange_weak(
current_active_clients_bit_mask, updated_active_clients_bit_mask,
std::memory_order_acq_rel, std::memory_order_acquire)) {
- ALOGE("Current active clients bit mask is changed to %" PRIx64
+ ALOGE("%s: Current active clients bit mask is changed to %" PRIx64
", which was expected to be %" PRIx64
". Trying to generate a new client state mask to resolve race "
"condition.",
- updated_active_clients_bit_mask, current_active_clients_bit_mask);
- client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
+ __FUNCTION__, updated_active_clients_bit_mask,
+ current_active_clients_bit_mask);
+ consumer_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
- if (client_state_mask == 0ULL) {
- ALOGE(
- "ProducerChannel::CreateConsumer: reached the maximum mumber of "
- "consumers per producer: 63.");
+ if (consumer_state_mask == 0ULL) {
+ ALOGE("%s: reached the maximum mumber of consumers per producer: %d.",
+ __FUNCTION__, (BufferHubDefs::kMaxNumberOfClients - 1));
return ErrorStatus(E2BIG);
}
updated_active_clients_bit_mask =
- current_active_clients_bit_mask | client_state_mask;
+ current_active_clients_bit_mask | consumer_state_mask;
}
- return {client_state_mask};
+ return {consumer_state_mask};
}
void ProducerChannel::RemoveConsumerClientMask(uint64_t consumer_state_mask) {
@@ -311,17 +309,14 @@
Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(
Message& message, uint64_t consumer_state_mask) {
- ATRACE_NAME("ProducerChannel::CreateConsumer");
- ALOGD_IF(TRACE,
- "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
- buffer_id(), producer_owns_);
+ ATRACE_NAME(__FUNCTION__);
+ ALOGD_IF(TRACE, "%s: buffer_id=%d", __FUNCTION__, buffer_id());
int channel_id;
auto status = message.PushChannel(0, nullptr, &channel_id);
if (!status) {
- ALOGE(
- "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
- status.GetErrorMessage().c_str());
+ ALOGE("%s: Failed to push consumer channel: %s", __FUNCTION__,
+ status.GetErrorMessage().c_str());
RemoveConsumerClientMask(consumer_state_mask);
return ErrorStatus(ENOMEM);
}
@@ -331,22 +326,51 @@
shared_from_this());
const auto channel_status = service()->SetChannel(channel_id, consumer);
if (!channel_status) {
- ALOGE(
- "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
- "%s",
- channel_status.GetErrorMessage().c_str());
+ ALOGE("%s: failed to set new consumer channel: %s.", __FUNCTION__,
+ channel_status.GetErrorMessage().c_str());
RemoveConsumerClientMask(consumer_state_mask);
return ErrorStatus(ENOMEM);
}
uint64_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (!producer_owns_ &&
- (BufferHubDefs::IsBufferPosted(current_buffer_state) ||
- BufferHubDefs::IsBufferAcquired(current_buffer_state))) {
- // Signal the new consumer when adding it to a posted producer.
- if (consumer->OnProducerPosted())
- pending_consumers_++;
+ if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ return {status.take()};
+ }
+
+ // Signal the new consumer when adding it to a posted producer.
+ bool update_buffer_state = true;
+ if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ consumer_state_mask)) {
+ uint64_t updated_buffer_state =
+ current_buffer_state ^
+ (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGI(
+ "%s: Failed to post to the new consumer. "
+ "Current buffer state was changed to %" PRIx64
+ " when trying to acquire the buffer and modify the buffer state to "
+ "%" PRIx64
+ ". About to try again if the buffer is still not gained nor fully "
+ "released.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ ALOGI("%s: buffer is gained or fully released, state=%" PRIx64 ".",
+ __FUNCTION__, current_buffer_state);
+ update_buffer_state = false;
+ break;
+ }
+ updated_buffer_state =
+ current_buffer_state ^
+ (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+ }
+ }
+ if (update_buffer_state) {
+ consumer->OnProducerPosted();
}
return {status.take()};
@@ -366,10 +390,6 @@
LocalFence acquire_fence) {
ATRACE_NAME("ProducerChannel::OnProducerPost");
ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
- if (!producer_owns_) {
- ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
- return ErrorStatus(EBUSY);
- }
epoll_event event;
event.events = 0;
@@ -400,18 +420,13 @@
dummy_fence_count, buffer_id());
post_fence_ = std::move(acquire_fence);
- producer_owns_ = false;
// Signal any interested consumers. If there are none, the buffer will stay
// in posted state until a consumer comes online. This behavior guarantees
// that no frame is silently dropped.
- pending_consumers_ = 0;
for (auto consumer : consumer_channels_) {
- if (consumer->OnProducerPosted())
- pending_consumers_++;
+ consumer->OnProducerPosted();
}
- ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
- pending_consumers_);
return {};
}
@@ -419,23 +434,8 @@
Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) {
ATRACE_NAME("ProducerChannel::OnGain");
ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
- channel_id());
- return ErrorStatus(EALREADY);
- }
-
- // There are still pending consumers, return busy.
- if (pending_consumers_ > 0) {
- ALOGE(
- "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
- "still has %d pending consumer(s).",
- buffer_id(), pending_consumers_);
- return ErrorStatus(EBUSY);
- }
ClearAvailable();
- producer_owns_ = true;
post_fence_.close();
return {std::move(returned_fence_)};
}
@@ -449,10 +449,12 @@
buffer_id());
uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (!BufferHubDefs::IsClientGained(
+ buffer_state, BufferHubDefs::kFirstClientStateMask)) {
// Can only detach a BufferProducer when it's in gained state.
ALOGW(
- "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+ "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=%"
+ PRIx64
") is not in gained state.",
buffer_id(), buffer_state);
return {};
@@ -485,12 +487,11 @@
const auto channel_status =
service()->SetChannel(channel_id, std::move(channel));
if (!channel_status) {
- // Technically, this should never fail, as we just pushed the channel. Note
- // that LOG_FATAL will be stripped out in non-debug build.
+ // Technically, this should never fail, as we just pushed the channel.
+ // Note that LOG_FATAL will be stripped out in non-debug build.
LOG_FATAL(
- "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
- "channel: %s.",
- channel_status.GetErrorMessage().c_str());
+ "ProducerChannel::OnProducerDetach: Failed to set new detached "
+ "buffer channel: %s.", channel_status.GetErrorMessage().c_str());
}
return status;
@@ -500,13 +501,9 @@
ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
- return ErrorStatus(EBUSY);
- }
- // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
- // Serialization just needs to read the handle.
+ // Return a borrowed fd to avoid unnecessary duplication of the underlying
+ // fd. Serialization just needs to read the handle.
return {std::move(post_fence_)};
}
@@ -515,10 +512,6 @@
ATRACE_NAME("ProducerChannel::OnConsumerRelease");
ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
- return ErrorStatus(EBUSY);
- }
// Attempt to merge the fences if necessary.
if (release_fence) {
@@ -538,75 +531,53 @@
}
}
- DecrementPendingConsumers();
- if (pending_consumers_ == 0) {
- // Clear the producer bit atomically to transit into released state. This
- // has to done by BufferHub as it requries synchronization among all
- // consumers.
- BufferHubDefs::ModifyBufferState(buffer_state_,
- BufferHubDefs::kFirstClientBitMask, 0ULL);
- ALOGD_IF(TRACE,
- "ProducerChannel::OnConsumerRelease: releasing last consumer: "
- "buffer_id=%d state=%" PRIx64 ".",
- buffer_id(), buffer_state_->load(std::memory_order_acquire));
-
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ ~orphaned_consumer_bit_mask_)) {
+ SignalAvailable();
if (orphaned_consumer_bit_mask_) {
ALOGW(
- "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
- "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
- " queue_index=%" PRIu64 ".",
- buffer_id(), orphaned_consumer_bit_mask_,
+ "%s: orphaned buffer detected during the this acquire/release cycle: "
+ "id=%d orphaned=0x%" PRIx64 " queue_index=%" PRIx64 ".",
+ __FUNCTION__, buffer_id(), orphaned_consumer_bit_mask_,
metadata_header_->queue_index);
orphaned_consumer_bit_mask_ = 0;
}
-
- SignalAvailable();
}
- ALOGE_IF(
- pending_consumers_ && BufferHubDefs::IsBufferReleased(
- buffer_state_->load(std::memory_order_acquire)),
- "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
- "pending_consumers=%d, buffer buffer is in releaed state.",
- pending_consumers_);
return {};
}
-void ProducerChannel::DecrementPendingConsumers() {
- if (pending_consumers_ == 0) {
- ALOGE("ProducerChannel::DecrementPendingConsumers: no pending consumer.");
- return;
+void ProducerChannel::OnConsumerOrphaned(const uint64_t& consumer_state_mask) {
+ // Remember the ignored consumer so that newly added consumer won't be
+ // taking the same state mask as this orphaned consumer.
+ ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_mask,
+ "%s: Consumer (consumer_state_mask=%" PRIx64
+ ") is already orphaned.",
+ __FUNCTION__, consumer_state_mask);
+ orphaned_consumer_bit_mask_ |= consumer_state_mask;
+
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ ~orphaned_consumer_bit_mask_)) {
+ SignalAvailable();
}
- --pending_consumers_;
- ALOGD_IF(TRACE,
- "ProducerChannel::DecrementPendingConsumers: buffer_id=%d %d "
- "consumers left",
- buffer_id(), pending_consumers_);
-}
-
-void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
- // Ignore the orphaned consumer.
- DecrementPendingConsumers();
-
- const uint64_t client_state_mask = channel->client_state_mask();
- ALOGE_IF(orphaned_consumer_bit_mask_ & client_state_mask,
- "ProducerChannel::OnConsumerOrphaned: Consumer "
- "(client_state_mask=%" PRIx64 ") is already orphaned.",
- client_state_mask);
- orphaned_consumer_bit_mask_ |= client_state_mask;
-
// Atomically clear the fence state bit as an orphaned consumer will never
- // signal a release fence. Also clear the buffer state as it won't be released
- // as well.
- fence_state_->fetch_and(~client_state_mask);
- BufferHubDefs::ModifyBufferState(buffer_state_, client_state_mask, 0ULL);
+ // signal a release fence.
+ fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+ // Atomically set the buffer state of this consumer to released state.
+ buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
ALOGW(
- "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
- "buffer_id=%d client_state_mask=%" PRIx64 " queue_index=%" PRIu64
+ "%s: detected new orphaned consumer buffer_id=%d "
+ "consumer_state_mask=%" PRIx64 " queue_index=%" PRIx64
" buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
- buffer_id(), client_state_mask, metadata_header_->queue_index,
+ __FUNCTION__, buffer_id(), consumer_state_mask,
+ metadata_header_->queue_index,
buffer_state_->load(std::memory_order_acquire),
fence_state_->load(std::memory_order_acquire));
}
@@ -620,45 +591,64 @@
std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
// Restore the consumer state bit and make it visible in other threads that
// acquire the active_clients_bit_mask_.
- active_clients_bit_mask_->fetch_and(~channel->client_state_mask(),
- std::memory_order_release);
+ uint64_t consumer_state_mask = channel->client_state_mask();
+ uint64_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ uint64_t updated_active_clients_bit_mask =
+ current_active_clients_bit_mask & (~consumer_state_mask);
+ while (!active_clients_bit_mask_->compare_exchange_weak(
+ current_active_clients_bit_mask, updated_active_clients_bit_mask,
+ std::memory_order_acq_rel, std::memory_order_acquire)) {
+ ALOGI(
+ "%s: Failed to remove consumer state mask. Current active clients bit "
+ "mask is changed to %" PRIu64
+ " when trying to acquire and modify it to %" PRIu64
+ ". About to try again.",
+ __FUNCTION__, current_active_clients_bit_mask,
+ updated_active_clients_bit_mask);
+ updated_active_clients_bit_mask =
+ current_active_clients_bit_mask & (~consumer_state_mask);
+ }
- const uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (BufferHubDefs::IsBufferPosted(buffer_state) ||
- BufferHubDefs::IsBufferAcquired(buffer_state)) {
+ const uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientPosted(current_buffer_state,
+ consumer_state_mask) ||
+ BufferHubDefs::IsClientAcquired(current_buffer_state,
+ consumer_state_mask)) {
// The consumer client is being destoryed without releasing. This could
// happen in corner cases when the consumer crashes. Here we mark it
// orphaned before remove it from producer.
- OnConsumerOrphaned(channel);
+ OnConsumerOrphaned(consumer_state_mask);
+ return;
}
- if (BufferHubDefs::IsBufferReleased(buffer_state) ||
- BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ consumer_state_mask) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state)) {
// The consumer is being close while it is suppose to signal a release
// fence. Signal the dummy fence here.
- if (fence_state_->load(std::memory_order_acquire) &
- channel->client_state_mask()) {
+ if (fence_state_->load(std::memory_order_acquire) & consumer_state_mask) {
epoll_event event;
event.events = EPOLLIN;
- event.data.u64 = channel->client_state_mask();
+ event.data.u64 = consumer_state_mask;
if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
dummy_fence_fd_.Get(), &event) < 0) {
ALOGE(
- "ProducerChannel::RemoveConsumer: Failed to modify the shared "
- "release fence to include the dummy fence: %s",
- strerror(errno));
+ "%s: Failed to modify the shared release fence to include the "
+ "dummy fence: %s",
+ __FUNCTION__, strerror(errno));
return;
}
- ALOGW(
- "ProducerChannel::RemoveConsumer: signal dummy release fence "
- "buffer_id=%d",
- buffer_id());
+ ALOGW("%s: signal dummy release fence buffer_id=%d", __FUNCTION__,
+ buffer_id());
eventfd_write(dummy_fence_fd_.Get(), 1);
}
}
}
-// Returns true if the given parameters match the underlying buffer parameters.
+// Returns true if the given parameters match the underlying buffer
+// parameters.
bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format,
uint64_t usage,
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index 6b5027c..6b33f50 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -319,7 +319,12 @@
return ErrorStatus(EINVAL);
}
uint64_t buffer_state = producer_channel->buffer_state();
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+ // TODO(b/112007999) add an atomic variable in metadata header in shared
+ // memory to indicate which client is the last producer of the buffer.
+ // Currently, the first client is the only producer to the buffer.
+ // Thus, it checks whether the first client gains the buffer below.
+ if (!BufferHubDefs::IsClientGained(buffer_state,
+ BufferHubDefs::kFirstClientBitMask)) {
// Rejects the request if the requested buffer is not in Gained state.
ALOGE(
"ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, "