Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b08b7226 authored by Tianyu Jiang's avatar Tianyu Jiang
Browse files

Implement changes of buffer state in Gain/Post/Acquire/Release methods in BufferHubBuffer.

Create shortcuts to atomics in BufferHubBuffer;
Implement changes of buffer state in the four usages of the buffer;
Create 16 unit tests for the 16 buffer state transitions.

Test: BufferHubBuffer_test
Bug: 118718711
Change-Id: I2067cd141611e66732e28344f26d73f261072b8b
parent 1a079e7f
Loading
Loading
Loading
Loading
+105 −3
Original line number Original line Diff line number Diff line
@@ -61,6 +61,16 @@ namespace {
// to use Binder.
// to use Binder.
static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client";
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
} // namespace


BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {}
BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {}
@@ -151,11 +161,17 @@ int BufferHubBuffer::ImportGraphicBuffer() {
    }
    }


    size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size());
    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);
        ALOGE("BufferHubBuffer::ImportGraphicBuffer: metadata too small: %zu", metadataSize);
        return -EINVAL;
        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
    // Import the buffer: We only need to hold on the native_handle_t here so that
    // GraphicBuffer instance can be created in future.
    // GraphicBuffer instance can be created in future.
    mBufferHandle = bufferTraits.take_buffer_handle();
    mBufferHandle = bufferTraits.take_buffer_handle();
@@ -176,7 +192,93 @@ int BufferHubBuffer::ImportGraphicBuffer() {


    // TODO(b/112012161) Set up shared fences.
    // TODO(b/112012161) Set up shared fences.
    ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(),
    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;
    return 0;
}
}


+28 −0
Original line number Original line Diff line number Diff line
@@ -103,6 +103,27 @@ public:
    // to read from and/or write into.
    // to read from and/or write into.
    bool IsValid() const { return mBufferHandle.IsValid(); }
    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
    // Returns the event mask for all the events that are pending on this buffer (see sys/poll.h for
    // all possible bits).
    // all possible bits).
    pdx::Status<int> GetEventMask(int events) {
    pdx::Status<int> GetEventMask(int events) {
@@ -130,6 +151,9 @@ private:


    // Global id for the buffer that is consistent across processes.
    // Global id for the buffer that is consistent across processes.
    int mId = -1;
    int mId = -1;

    // Client state mask of this BufferHubBuffer object. It is unique amoung all
    // clients/users of the buffer.
    uint64_t mClientStateMask = 0;
    uint64_t mClientStateMask = 0;


    // Stores ground truth of the buffer.
    // Stores ground truth of the buffer.
@@ -141,6 +165,10 @@ private:
    // An ashmem-based metadata object. The same shared memory are mapped to the
    // An ashmem-based metadata object. The same shared memory are mapped to the
    // bufferhubd daemon and all buffer clients.
    // bufferhubd daemon and all buffer clients.
    BufferHubMetadata mMetadata;
    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.
    // PDX backend.
    BufferHubClient mClient;
    BufferHubClient mClient;
+213 −0
Original line number Original line Diff line number Diff line
@@ -36,7 +36,14 @@ const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
const int kUsage = 0;
const int kUsage = 0;
const size_t kUserMetadataSize = 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::IsBufferReleased;
using dvr::BufferHubDefs::IsClientAcquired;
using dvr::BufferHubDefs::IsClientGained;
using dvr::BufferHubDefs::IsClientPosted;
using dvr::BufferHubDefs::IsClientReleased;
using dvr::BufferHubDefs::kFirstClientBitMask;
using dvr::BufferHubDefs::kFirstClientBitMask;
using dvr::BufferHubDefs::kMetadataHeaderSize;
using dvr::BufferHubDefs::kMetadataHeaderSize;
using frameworks::bufferhub::V1_0::BufferHubStatus;
using frameworks::bufferhub::V1_0::BufferHubStatus;
@@ -48,9 +55,40 @@ using hidl::base::V1_0::IBase;
using pdx::LocalChannelHandle;
using pdx::LocalChannelHandle;


class BufferHubBufferTest : public ::testing::Test {
class BufferHubBufferTest : public ::testing::Test {
protected:
    void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); }
    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) {
TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) {
    // Buffer Creation will fail: BLOB format requires height to be 1.
    // Buffer Creation will fail: BLOB format requires height to be 1.
    auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount,
    auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount,
@@ -315,5 +353,180 @@ TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
    EXPECT_EQ(nullptr, client2.get());
    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
} // namespace android
} // namespace android