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

Commit c90a77f1 authored by Jiwen 'Steve' Cai's avatar Jiwen 'Steve' Cai
Browse files

Add BufferHub backend for android::view::Surface

This CL allows BufferHubProducer to be used as alternative backend of
parcelable Surface.

When sent over binder, BufferHubProducer serializes itself differently
from Binder-based IGBP objects. Instead of writing to a Parcel object as
strong binder object, BufferHubProducer asks libbufferhubqueue to
generate a BufferHubQueueParcelable object (which packs all the FDs
representing the BufferHub channel).

When received from a binder interface, BufferHubProducer object can be
reconstructed from the BufferHubQueueParcelable object. The newly
constructed object has all the FDs (i.e. UDS channels) directly
connected to bufferhubd. Thus, on going buffer transport operations can
happen directly between the receiving process and bufferhubd. This
elimates one extra binder hop.

Bug: 37517761
Bug: 70046255
Test: libgui_test, buffer_transport_benchmark,
      buffer_hub_queue_producer-test, dvr_api-test,
      SurfaceParcelable_test
Change-Id: I78bd879f36d3196f3d74c76c79d27467740792f7
parent 5a8f0d49
Loading
Loading
Loading
Loading
+38 −12
Original line number Diff line number Diff line
@@ -35,8 +35,10 @@

namespace android {

using namespace dvr;

/* static */
sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<dvr::ProducerQueue>& queue) {
sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<ProducerQueue>& queue) {
    if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
        ALOGE("BufferHubProducer::Create producer's metadata size is different "
              "than the size of DvrNativeBufferMetadata");
@@ -49,14 +51,14 @@ sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<dvr::Produ
}

/* static */
sp<BufferHubProducer> BufferHubProducer::Create(dvr::ProducerQueueParcelable parcelable) {
sp<BufferHubProducer> BufferHubProducer::Create(ProducerQueueParcelable parcelable) {
    if (!parcelable.IsValid()) {
        ALOGE("BufferHubProducer::Create: Invalid producer parcelable.");
        return nullptr;
    }

    sp<BufferHubProducer> producer = new BufferHubProducer;
    producer->queue_ = dvr::ProducerQueue::Import(parcelable.TakeChannelHandle());
    producer->queue_ = ProducerQueue::Import(parcelable.TakeChannelHandle());
    return producer;
}

@@ -102,9 +104,9 @@ status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers)

    if (max_dequeued_buffers <= 0 ||
        max_dequeued_buffers >
                int(dvr::BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) {
                int(BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) {
        ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers,
              dvr::BufferHubQueue::kMaxQueueCapacity);
              BufferHubQueue::kMaxQueueCapacity);
        return BAD_VALUE;
    }

@@ -153,7 +155,7 @@ status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, u
                                          uint32_t height, PixelFormat format, uint64_t usage,
                                          uint64_t* /*outBufferAge*/,
                                          FrameEventHistoryDelta* /* out_timestamps */) {
    ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage);
    ALOGW("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage);

    status_t ret;
    std::unique_lock<std::mutex> lock(mutex_);
@@ -174,9 +176,9 @@ status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, u
    }

    size_t slot = 0;
    std::shared_ptr<dvr::BufferProducer> buffer_producer;
    std::shared_ptr<BufferProducer> buffer_producer;

    for (size_t retry = 0; retry < dvr::BufferHubQueue::kMaxQueueCapacity; retry++) {
    for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
        LocalHandle fence;
        auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
        if (!buffer_status) return NO_MEMORY;
@@ -225,7 +227,7 @@ status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, u

    buffers_[slot].mBufferState.freeQueued();
    buffers_[slot].mBufferState.dequeue();
    ALOGV("dequeueBuffer: slot=%zu", slot);
    ALOGW("dequeueBuffer: slot=%zu", slot);

    // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
    // just need to exopose that through |BufferHubQueue| once we need fence.
@@ -590,7 +592,7 @@ status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) {
    ALOGV(__FUNCTION__);

    std::unique_lock<std::mutex> lock(mutex_);
    dequeue_timeout_ms_ = int(timeout / (1000 * 1000));
    dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
    return NO_ERROR;
}

@@ -620,7 +622,7 @@ status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const {
    return NO_ERROR;
}

status_t BufferHubProducer::TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable) {
status_t BufferHubProducer::TakeAsParcelable(ProducerQueueParcelable* out_parcelable) {
    if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE;

    if (connected_api_ != kNoConnectedApi) {
@@ -684,7 +686,7 @@ status_t BufferHubProducer::RemoveBuffer(size_t slot) {
}

status_t BufferHubProducer::FreeAllBuffers() {
    for (size_t slot = 0; slot < dvr::BufferHubQueue::kMaxQueueCapacity; slot++) {
    for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
        // Reset in memory objects related the the buffer.
        buffers_[slot].mGraphicBuffer = nullptr;
        buffers_[slot].mBufferState.reset();
@@ -707,4 +709,28 @@ status_t BufferHubProducer::FreeAllBuffers() {
    return NO_ERROR;
}

status_t BufferHubProducer::exportToParcel(Parcel* parcel) {
    status_t res = TakeAsParcelable(&pending_producer_parcelable_);
    if (res != NO_ERROR) return res;

    if (!pending_producer_parcelable_.IsValid()) {
        ALOGE("BufferHubProducer::exportToParcel: Invalid parcelable object.");
        return BAD_VALUE;
    }

    res = parcel->writeUint32(USE_BUFFER_HUB);
    if (res != NO_ERROR) {
        ALOGE("BufferHubProducer::exportToParcel: Cannot write magic, res=%d.", res);
        return res;
    }

    return pending_producer_parcelable_.writeToParcel(parcel);
}

IBinder* BufferHubProducer::onAsBinder() {
    ALOGE("BufferHubProducer::onAsBinder: BufferHubProducer should never be used as an Binder "
          "object.");
    return nullptr;
}

} // namespace android
+70 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#include <gui/BufferHubProducer.h>
#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
@@ -653,6 +654,75 @@ IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,

// ----------------------------------------------------------------------

status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
    status_t res = OK;
    res = parcel->writeUint32(USE_BUFFER_QUEUE);
    if (res != NO_ERROR) {
        ALOGE("exportToParcel: Cannot write magic, res=%d.", res);
        return res;
    }

    return parcel->writeStrongBinder(IInterface::asBinder(this));
}

/* static */
status_t IGraphicBufferProducer::exportToParcel(const sp<IGraphicBufferProducer>& producer,
                                                Parcel* parcel) {
    if (parcel == nullptr) {
        ALOGE("exportToParcel: Invalid parcel object.");
        return BAD_VALUE;
    }

    if (producer == nullptr) {
        status_t res = OK;
        res = parcel->writeUint32(IGraphicBufferProducer::USE_BUFFER_QUEUE);
        if (res != NO_ERROR) return res;
        return parcel->writeStrongBinder(nullptr);
    } else {
        return producer->exportToParcel(parcel);
    }
}

/* static */
sp<IGraphicBufferProducer> IGraphicBufferProducer::createFromParcel(const Parcel* parcel) {
    uint32_t outMagic = 0;
    status_t res = NO_ERROR;

    res = parcel->readUint32(&outMagic);
    if (res != NO_ERROR) {
        ALOGE("createFromParcel: Failed to read magic, error=%d.", res);
        return nullptr;
    }

    switch (outMagic) {
        case USE_BUFFER_QUEUE: {
            sp<IBinder> binder;
            res = parcel->readNullableStrongBinder(&binder);
            if (res != NO_ERROR) {
                ALOGE("createFromParcel: Can't read strong binder.");
                return nullptr;
            }
            return interface_cast<IGraphicBufferProducer>(binder);
        }
        case USE_BUFFER_HUB: {
            ALOGE("createFromParcel: BufferHub not implemented.");
            dvr::ProducerQueueParcelable producerParcelable;
            res = producerParcelable.readFromParcel(parcel);
            if (res != NO_ERROR) {
                ALOGE("createFromParcel: Failed to read from parcel, error=%d", res);
                return nullptr;
            }
            return BufferHubProducer::Create(std::move(producerParcelable));
        }
        default: {
            ALOGE("createFromParcel: Unexpected mgaic: 0x%x.", outMagic);
            return nullptr;
        }
    }
}

// ----------------------------------------------------------------------------

status_t BnGraphicBufferProducer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
+23 −1
Original line number Diff line number Diff line
@@ -19,12 +19,25 @@

#include <gui/BufferSlot.h>
#include <gui/IGraphicBufferProducer.h>

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif

// The following headers are included without checking every warning.
// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
// in these headers and their dependencies.
#include <private/dvr/buffer_hub_queue_client.h>
#include <private/dvr/buffer_hub_queue_parcelable.h>

#if defined(__clang__)
#pragma clang diagnostic pop
#endif

namespace android {

class BufferHubProducer : public BnGraphicBufferProducer {
class BufferHubProducer : public IGraphicBufferProducer {
public:
    static constexpr int kNoConnectedApi = -1;

@@ -135,6 +148,12 @@ public:
    // invalid parcelable.
    status_t TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable);

    IBinder* onAsBinder() override;

protected:
    // See |IGraphicBufferProducer::exportToParcel|
    status_t exportToParcel(Parcel* parcel) override;

private:
    using LocalHandle = pdx::LocalHandle;

@@ -203,6 +222,9 @@ private:

    // A uniqueId used by IGraphicBufferProducer interface.
    const uint64_t unique_id_{genUniqueId()};

    // A pending parcelable object which keeps the bufferhub channel alive.
    dvr::ProducerQueueParcelable pending_producer_parcelable_;
};

} // namespace android
+26 −0
Original line number Diff line number Diff line
@@ -73,6 +73,14 @@ public:
        RELEASE_ALL_BUFFERS       = 0x2,
    };

    enum {
        // A parcelable magic indicates using Binder BufferQueue as transport
        // backend.
        USE_BUFFER_QUEUE = 0x62717565, // 'bque'
        // A parcelable magic indicates using BufferHub as transport backend.
        USE_BUFFER_HUB = 0x62687562, // 'bhub'
    };

    // requestBuffer requests a new buffer for the given index. The server (i.e.
    // the IGraphicBufferProducer implementation) assigns the newly created
    // buffer to the given slot index, and the client is expected to mirror the
@@ -604,6 +612,24 @@ public:
    // returned by querying the now deprecated
    // NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute.
    virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0;

    // Static method exports any IGraphicBufferProducer object to a parcel. It
    // handles null producer as well.
    static status_t exportToParcel(const sp<IGraphicBufferProducer>& producer,
                                   Parcel* parcel);

    // Factory method that creates a new IBGP instance from the parcel.
    static sp<IGraphicBufferProducer> createFromParcel(const Parcel* parcel);

protected:
    // Exports the current producer as a binder parcelable object. Note that the
    // producer must be disconnected to be exportable. After successful export,
    // the producer queue can no longer be connected again. Returns NO_ERROR
    // when the export is successful and writes an implementation defined
    // parcelable object into the parcel. For traditional Android BufferQueue,
    // it writes a strong binder object; for BufferHub, it writes a
    // ProducerQueueParcelable object.
    virtual status_t exportToParcel(Parcel* parcel);
};

// ----------------------------------------------------------------------------
+32 −0
Original line number Diff line number Diff line
@@ -49,3 +49,35 @@ cc_test {
        "libnativewindow"
    ],
}

// Build a separate binary for each source file to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
cc_test {
    name: "libgui_separate_binary_test",
    test_suites: ["device-tests"],

    clang: true,
    cflags: [
        "-Wall",
        "-Werror",
    ],

    test_per_src: true,
    srcs: [
        "SurfaceParcelable_test.cpp",
    ],

    shared_libs: [
        "liblog",
        "libbinder",
        "libcutils",
        "libgui",
        "libui",
        "libutils",
        "libbufferhubqueue",  // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
        "libpdx_default_transport",
    ],

    header_libs: [
        "libdvr_headers",
    ],
}
Loading