Merge "Add buffer transport benchmark"
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index a610a51..102964f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -118,4 +118,7 @@
vendor_available: true,
}
-subdirs = ["tests"]
+subdirs = [
+ "tests",
+ "tools",
+]
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index f327200..e3cb494 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -61,10 +61,10 @@
cc_test {
tags: ["optional"],
- srcs: ["bufferhub_tests.cpp"],
+ srcs: ["buffer_hub-test.cpp"],
static_libs: ["libbufferhub"] + staticLibraries,
shared_libs: sharedLibraries,
header_libs: headerLibraries,
- name: "bufferhub_tests",
+ name: "buffer_hub-test",
}
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
similarity index 68%
rename from libs/vr/libbufferhub/bufferhub_tests.cpp
rename to libs/vr/libbufferhub/buffer_hub-test.cpp
index c4b9a8c..3c99f99 100644
--- a/libs/vr/libbufferhub/bufferhub_tests.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -20,6 +20,10 @@
using android::dvr::BufferConsumer;
using android::dvr::BufferHubDefs::kConsumerStateMask;
using android::dvr::BufferHubDefs::kProducerStateBit;
+using android::dvr::BufferHubDefs::IsBufferGained;
+using android::dvr::BufferHubDefs::IsBufferPosted;
+using android::dvr::BufferHubDefs::IsBufferAcquired;
+using android::dvr::BufferHubDefs::IsBufferReleased;
using android::dvr::BufferProducer;
using android::pdx::LocalHandle;
@@ -28,6 +32,7 @@
const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
const int kUsage = 0;
const uint64_t kContext = 42;
+const size_t kMaxConsumerCount = 63;
using LibBufferHubTest = ::testing::Test;
@@ -159,10 +164,10 @@
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- // It's ok to create up to 63 consumer buffers.
+ // It's ok to create up to kMaxConsumerCount consumer buffers.
uint64_t buffer_state_bits = p->buffer_state_bit();
- std::array<std::unique_ptr<BufferConsumer>, 63> cs;
- for (size_t i = 0; i < 63; i++) {
+ std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
cs[i] = BufferConsumer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
// Expect all buffers have unique state mask.
@@ -176,7 +181,7 @@
EXPECT_EQ(state.error(), E2BIG);
// Release any consumer should allow us to re-create.
- for (size_t i = 0; i < 63; i++) {
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
buffer_state_bits &= ~cs[i]->buffer_state_bit();
cs[i] = nullptr;
cs[i] = BufferConsumer::Import(p->CreateConsumer());
@@ -240,6 +245,217 @@
EXPECT_EQ(-EALREADY, p->Gain(&fence));
}
+TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // The producer buffer starts in gained state.
+
+ // Acquire, release, 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());
+
+ // 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()));
+
+ // Post, release, 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());
+
+ // Acquire in posted state should succeed.
+ EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+ 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()));
+
+ // Acquire, post, and gain in acquired state should fail.
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+
+ // Release in acquired state should succeed.
+ EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+ 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));
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+
+ // Gain in released state should succeed.
+ 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()));
+
+ // Acquire, release, 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, TestZeroConsumer) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ DvrNativeBufferMetadata metadata;
+ 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(100)));
+
+ // A new consumer should still be able to acquire the buffer immediately.
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestMaxConsumers) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ cs[i] = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(cs[i].get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+ }
+
+ 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(IsBufferPosted(cs[i]->buffer_state(),
+ cs[i]->buffer_state_bit()));
+ EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(10)));
+ EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ }
+
+ // All consumers have to release before the buffer is considered to be
+ // released.
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+ EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
+ }
+
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+
+ // Buffer state cross all clients must be consistent.
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_EQ(p->buffer_state(), cs[i]->buffer_state());
+ }
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(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_LT(0, RETRY_EINTR(c->Poll(10)));
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Post the gained buffer before any consumer gets created.
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+ // Newly created consumer should be automatically sigalled.
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ std::unique_ptr<BufferConsumer> c1 =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c1.get() != nullptr);
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Post, acquire, and release the buffer..
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_LT(0, RETRY_EINTR(c1->Poll(10)));
+ EXPECT_EQ(0, c1->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_EQ(0, c1->ReleaseAsync(&metadata, invalid_fence));
+
+ // Create another consumer immediately after the release, should not make the
+ // buffer un-released. This is guaranteed by IPC execution order in bufferhubd.
+ std::unique_ptr<BufferConsumer> c2 =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+}
+
TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
struct Metadata {
int64_t field1;
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
index 1186f93..a356959 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -94,6 +94,9 @@
int id() const { return id_; }
+ // Returns the buffer buffer state.
+ uint64_t buffer_state() { return buffer_state_->load(); };
+
// A state mask which is unique to a buffer hub client among all its siblings
// sharing the same concrete graphic buffer.
uint64_t buffer_state_bit() const { return buffer_state_bit_; }
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 7d6d988..c707e3c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -223,6 +223,10 @@
mWriter.reset();
}
+Error Composer::executeCommands() {
+ return execute();
+}
+
uint32_t Composer::getMaxVirtualDisplayCount()
{
auto ret = mClient->getMaxVirtualDisplayCount();
@@ -750,6 +754,11 @@
}
}
+ if (commandLength == 0) {
+ mWriter.reset();
+ return Error::NONE;
+ }
+
Error error = kDefaultError;
auto ret = mClient->executeCommands(commandLength, commandHandles,
[&](const auto& tmpError, const auto& tmpOutChanged,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 31a3c1d..104ca60 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -152,6 +152,9 @@
// skip a frame but have already queued some commands.
void resetCommands();
+ // Explicitly flush all pending commands in the command buffer.
+ Error executeCommands();
+
uint32_t getMaxVirtualDisplayCount();
bool isUsingVrComposer() const { return mIsUsingVrComposer; }
Error createVirtualDisplay(uint32_t width, uint32_t height,
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 78c0c85..ab4a4b2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -221,6 +221,11 @@
}
}
+Error Device::flushCommands()
+{
+ return static_cast<Error>(mComposer->executeCommands());
+}
+
// Display methods
Display::Display(android::Hwc2::Composer& composer,
@@ -632,11 +637,6 @@
return error;
}
-void Display::discardCommands()
-{
- mComposer.resetCommands();
-}
-
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index fbe4c7e..a15c6d9 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -106,6 +106,11 @@
android::Hwc2::Composer* getComposer() { return mComposer.get(); }
+ // We buffer most state changes and flush them implicitly with
+ // Display::validate, Display::present, and Display::presentOrValidate.
+ // This method provides an explicit way to flush state changes to HWC.
+ Error flushCommands();
+
private:
// Initialization methods
@@ -244,12 +249,6 @@
uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state);
- // Most methods in this class write a command to a command buffer. The
- // command buffer is implicitly submitted in validate, present, and
- // presentOrValidate. This method provides a way to discard the commands,
- // which can be used to discard stale commands.
- void discardCommands();
-
// Other Display methods
hwc2_display_t getId() const { return mId; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b096a3a..5328a22 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -605,8 +605,11 @@
auto& hwcDisplay = displayData.hwcDisplay;
if (displayData.validateWasSkipped) {
- hwcDisplay->discardCommands();
- auto error = displayData.presentError;
+ // explicitly flush all pending commands
+ auto error = mHwcDevice->flushCommands();
+ if (displayData.presentError != HWC2::Error::None) {
+ error = displayData.presentError;
+ }
if (error != HWC2::Error::None) {
ALOGE("skipValidate: failed for display %d: %s (%d)",
displayId, to_string(error).c_str(), static_cast<int32_t>(error));