diff options
| -rw-r--r-- | libs/ui/BufferHubBuffer.cpp | 108 | ||||
| -rw-r--r-- | libs/ui/include/ui/BufferHubBuffer.h | 28 | ||||
| -rw-r--r-- | libs/ui/tests/BufferHubBuffer_test.cpp | 213 |
3 files changed, 346 insertions, 3 deletions
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp index e747ee11be..3816c1bc4f 100644 --- a/libs/ui/BufferHubBuffer.cpp +++ b/libs/ui/BufferHubBuffer.cpp @@ -61,6 +61,16 @@ namespace { // to use Binder. static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client"; +using dvr::BufferHubDefs::AnyClientAcquired; +using dvr::BufferHubDefs::AnyClientGained; +using dvr::BufferHubDefs::AnyClientPosted; +using dvr::BufferHubDefs::IsClientAcquired; +using dvr::BufferHubDefs::IsClientGained; +using dvr::BufferHubDefs::IsClientPosted; +using dvr::BufferHubDefs::IsClientReleased; +using dvr::BufferHubDefs::kHighBitsMask; +using dvr::BufferHubDefs::kMetadataHeaderSize; + } // namespace BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {} @@ -71,7 +81,7 @@ BufferHubClient::BufferHubClient(LocalChannelHandle mChannelHandle) BufferHubClient::~BufferHubClient() {} bool BufferHubClient::IsValid() const { - return IsConnected() && GetChannelHandle().valid(); + return IsConnected() && GetChannelHandle().valid(); } LocalChannelHandle BufferHubClient::TakeChannelHandle() { @@ -151,11 +161,17 @@ int BufferHubBuffer::ImportGraphicBuffer() { } size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size()); - if (metadataSize < dvr::BufferHubDefs::kMetadataHeaderSize) { + if (metadataSize < kMetadataHeaderSize) { ALOGE("BufferHubBuffer::ImportGraphicBuffer: metadata too small: %zu", metadataSize); return -EINVAL; } + // Populate shortcuts to the atomics in metadata. + auto metadata_header = mMetadata.metadata_header(); + buffer_state_ = &metadata_header->buffer_state; + fence_state_ = &metadata_header->fence_state; + active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask; + // Import the buffer: We only need to hold on the native_handle_t here so that // GraphicBuffer instance can be created in future. mBufferHandle = bufferTraits.take_buffer_handle(); @@ -176,7 +192,93 @@ int BufferHubBuffer::ImportGraphicBuffer() { // TODO(b/112012161) Set up shared fences. ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(), - mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire)); + buffer_state_->load(std::memory_order_acquire)); + return 0; +} + +int BufferHubBuffer::Gain() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already gained by this client %" PRIx64 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + do { + if (AnyClientGained(current_buffer_state & (~mClientStateMask)) || + AnyClientAcquired(current_buffer_state)) { + ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx64 " state=%" PRIx64 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state to gained state, whose value happens to be the same as + // mClientStateMask. + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, mClientStateMask, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::Post() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + uint64_t current_active_clients_bit_mask = 0ULL; + uint64_t updated_buffer_state = 0ULL; + do { + if (!IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d " + "mClientStateMask=%" PRIx64 " state=%" PRIx64 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Set the producer client buffer state to released, other clients' buffer state to posted. + current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire); + updated_buffer_state = + current_active_clients_bit_mask & (~mClientStateMask) & kHighBitsMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. + return 0; +} + +int BufferHubBuffer::Acquire() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientAcquired(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already acquired by this client %" PRIx64 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint64_t updated_buffer_state = 0ULL; + do { + if (!IsClientPosted(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d " + "mClientStateMask=%" PRIx64 " state=%" PRIx64 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state for this consumer from posted to acquired. + updated_buffer_state = current_buffer_state ^ mClientStateMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::Release() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientReleased(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already released by this client %" PRIx64 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint64_t updated_buffer_state = 0ULL; + do { + updated_buffer_state = current_buffer_state & (~mClientStateMask); + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. return 0; } diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h index 6850b43f8a..03d10e7a9a 100644 --- a/libs/ui/include/ui/BufferHubBuffer.h +++ b/libs/ui/include/ui/BufferHubBuffer.h @@ -103,6 +103,27 @@ public: // to read from and/or write into. bool IsValid() const { return mBufferHandle.IsValid(); } + // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is + // gained. + // The buffer can be gained as long as there is no other client in acquired or gained state. + int Gain(); + + // Posts the gained buffer for other buffer clients to use the buffer. + // The buffer can be posted iff the buffer state for this client is gained. + // After posting the buffer, this client is put to released state and does not have access to + // the buffer for this cycle of the usage of the buffer. + int Post(); + + // Acquires the buffer for shared read permission. + // The buffer can be acquired iff the buffer state for this client is posted. + int Acquire(); + + // Releases the buffer. + // The buffer can be released from any buffer state. + // After releasing the buffer, this client no longer have any permissions to the buffer for the + // current cycle of the usage of the buffer. + int Release(); + // Returns the event mask for all the events that are pending on this buffer (see sys/poll.h for // all possible bits). pdx::Status<int> GetEventMask(int events) { @@ -130,6 +151,9 @@ private: // Global id for the buffer that is consistent across processes. int mId = -1; + + // Client state mask of this BufferHubBuffer object. It is unique amoung all + // clients/users of the buffer. uint64_t mClientStateMask = 0; // Stores ground truth of the buffer. @@ -141,6 +165,10 @@ private: // An ashmem-based metadata object. The same shared memory are mapped to the // bufferhubd daemon and all buffer clients. BufferHubMetadata mMetadata; + // Shortcuts to the atomics inside the header of mMetadata. + std::atomic<uint64_t>* buffer_state_{nullptr}; + std::atomic<uint64_t>* fence_state_{nullptr}; + std::atomic<uint64_t>* active_clients_bit_mask_{nullptr}; // PDX backend. BufferHubClient mClient; diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp index 6c7d06bd0d..e33acf6d11 100644 --- a/libs/ui/tests/BufferHubBuffer_test.cpp +++ b/libs/ui/tests/BufferHubBuffer_test.cpp @@ -36,7 +36,14 @@ const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; const size_t kUserMetadataSize = 0; +using dvr::BufferHubDefs::AnyClientAcquired; +using dvr::BufferHubDefs::AnyClientGained; +using dvr::BufferHubDefs::AnyClientPosted; using dvr::BufferHubDefs::IsBufferReleased; +using dvr::BufferHubDefs::IsClientAcquired; +using dvr::BufferHubDefs::IsClientGained; +using dvr::BufferHubDefs::IsClientPosted; +using dvr::BufferHubDefs::IsClientReleased; using dvr::BufferHubDefs::kFirstClientBitMask; using dvr::BufferHubDefs::kMetadataHeaderSize; using frameworks::bufferhub::V1_0::BufferHubStatus; @@ -48,9 +55,40 @@ using hidl::base::V1_0::IBase; using pdx::LocalChannelHandle; class BufferHubBufferTest : public ::testing::Test { +protected: void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); } }; +class BufferHubBufferStateTransitionTest : public BufferHubBufferTest { +protected: + void SetUp() override { + BufferHubBufferTest::SetUp(); + CreateTwoClientsOfABuffer(); + } + + std::unique_ptr<BufferHubBuffer> b1; + uint64_t b1ClientMask = 0ULL; + std::unique_ptr<BufferHubBuffer> b2; + uint64_t b2ClientMask = 0ULL; + +private: + // Creates b1 and b2 as the clients of the same buffer for testing. + void CreateTwoClientsOfABuffer(); +}; + +void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() { + b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + b1ClientMask = b1->client_state_mask(); + ASSERT_NE(b1ClientMask, 0ULL); + auto statusOrHandle = b1->Duplicate(); + ASSERT_TRUE(statusOrHandle); + LocalChannelHandle h2 = statusOrHandle.take(); + b2 = BufferHubBuffer::Import(std::move(h2)); + b2ClientMask = b2->client_state_mask(); + ASSERT_NE(b2ClientMask, 0ULL); + ASSERT_NE(b2ClientMask, b1ClientMask); +} + TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) { // Buffer Creation will fail: BLOB format requires height to be 1. auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount, @@ -315,5 +353,180 @@ TEST_F(BufferHubBufferTest, ImportFreedBuffer) { EXPECT_EQ(nullptr, client2.get()); } +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Successful gaining the buffer should change the buffer state bit of b1 to + // gained state, other client state bits to released state. + EXPECT_EQ(b1->Gain(), 0); + EXPECT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientGained(current_buffer_state, b1ClientMask)); + + // Gaining from gained state by the same client should not return error. + EXPECT_EQ(b1->Gain(), 0); + + // Gaining from gained state by another client should return error. + EXPECT_EQ(b2->Gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + // Gaining from acquired state should fail. + EXPECT_EQ(b1->Gain(), -EBUSY); + EXPECT_EQ(b2->Gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // Gaining a buffer who has other posted client should succeed. + EXPECT_EQ(b1->Gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // A posted client should be able to gain the buffer when there is no other clients in + // acquired state. + EXPECT_EQ(b2->Gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b1->Post(), 0); + auto current_buffer_state = b1->buffer_state(); + EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask)); + EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // Post from posted state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + // Posting from acquired state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Posting from released state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask)); + + // Acquire from posted state should pass. + EXPECT_EQ(b2->Acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask)); + + // Acquire from released state should fail, although there are other clients + // in posted state. + EXPECT_EQ(b1->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientAcquired(current_buffer_state, b2ClientMask)); + + // Acquiring from acquired state by the same client should not error out. + EXPECT_EQ(b2->Acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Acquiring form released state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + // Acquiring from gained state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 0); +} + } // namespace } // namespace android |