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

Commit 65cd76ed authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "c2aidl: GraphicsTracker implementation" into main

parents e9ba1054 f075f719
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ cc_library {
    name: "libcodec2_client",

    srcs: [
        "GraphicsTracker.cpp",
        "client.cpp",
        "output.cpp",
    ],
@@ -50,6 +51,7 @@ cc_library {
        "libgui",
        "libhidlbase",
        "liblog",
        "libnativewindow",
        "libstagefright_bufferpool@2.0.1",
        "libui",
        "libutils",
+836 −0

File added.

Preview size limit exceeded, changes collapsed.

+306 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

#pragma once

#include <android/hardware_buffer.h>
#include <android-base/unique_fd.h>
#include <gui/IGraphicBufferProducer.h>

#include <atomic>
#include <condition_variable>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <thread>

#include <C2Buffer.h>

namespace aidl::android::hardware::media::c2::implementation {

using ::android::IGraphicBufferProducer;
using ::android::GraphicBuffer;
using ::android::Fence;
using ::android::PixelFormat;
using ::android::sp;
/**
 * The class allocates AHardwareBuffer(GraphicBuffer)s using BufferQueue.
 *
 * The class tracks and manages outstanding # of allocations for buffer
 * recycling. So Graphics operations which affects # of outstanding allocation
 * should be done via the class. (e.g. rendering a buffer to display)
 *
 * The class is supposed to be wrapped into IGraphicBufferAllocator AIDL interface,
 * and the interface will be passed to HAL for a specific BlockPool instance.
 *
 * The class has one to one relation with HAL side Graphic C2BlockPool.
 * The life cycle of the class is tied to a HAL side BlockPool object.
 *
 * So, reset()/stop() of HAL which related to blokcpool destruction will terminate the
 * use of the class. And a new instance should be created in order for start()
 * of HAL.
 */
class GraphicsTracker {
public:
    static std::shared_ptr<GraphicsTracker> CreateGraphicsTracker(int maxDequeueCount) {
        GraphicsTracker *p = new GraphicsTracker(maxDequeueCount);
        std::shared_ptr<GraphicsTracker> sp(p);
        return sp;
    }

    ~GraphicsTracker();

    /**
     * Configure a new surface to render/allocate graphic blocks.
     *
     * Graphic  blocks from the old surface will be migrated to the new surface,
     * if possible. Configuring to a null surface is possible in the case,
     * an allocation request will be fulfilled by a direct allocation(not using
     * BQ). generation should be different to the previous generations.
     *
     * @param[in] igbp        the new surface to configure
     * @param[in] generation  identifier for each configured surface
     */
    c2_status_t configureGraphics(const sp<IGraphicBufferProducer>& igbp, uint32_t generation);

    /**
     * Configure max # of outstanding allocations at any given time.
     *
     * @param[in] maxDequeueCount    max # of outstanding allocation to configure
     */
    c2_status_t configureMaxDequeueCount(int maxDequeueCount);

    /**
     * Allocates a AHardwareBuffer.
     *
     * @param[in] width       width
     * @param[in] height      height
     * @param[in] PixelFormat pixel format which describes color format and etc
     * @param[in] usage       gralloc usage bits
     * @param[out] buf        the allocated buffer
     * @param[out] fence      fence for the allocated buffer
     * @return  C2_OK         the buffer is allocated
     *          C2_BAD_STATE  stop() is called and in stopped state
     *          C2_BLOCKING   should be waited to allocate
     *          C2_NO_MEMORY  out of memory
     *          C2_CORRUPTED
     */
    c2_status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
                         AHardwareBuffer **buf, sp<Fence> *fence);

    /**
     * Deallocates a AHardwareBuffer
     *
     * @param[in] bufId         id of the buffer to deallocate
     * @param[in] fence         i/o fence for the buffer
     * @return  C2_OK           the buffer is successfully deallocated.
     *          C2_DUPLICATE    deallocation/render request is pending already.
     *          C2_NOT_FOUND    the buffer with the id is not allocated.
     */
    c2_status_t deallocate(uint64_t bufId, const sp<Fence> &fence);

    /**
     * Render a GraphicBlock which is associated to a pending allocated buffer
     *
     * @param[in] block         GraphicBlock
     * @param[in] input         render input params to Graphics
     * @param[out] output       render output params from Graphics
     * @return  C2_OK           the buffer is now ready to render
     *          C2_BAD_STATE    there is no surface to render.
     *                          (null surface mode or life cycle ends)
     *          C2_DUPLICATE    deallocation/render request is pending already.
     *          C2_NOT_FOUND    the buffer with the id is not allocated.
     *          C2_REFUSED      the buffer is refused to render from Graphics
     *          C2_CORRUPTED
     */
    c2_status_t render(const C2ConstGraphicBlock& block,
                       const IGraphicBufferProducer::QueueBufferInput& input,
                       IGraphicBufferProducer::QueueBufferOutput *output);

    /**
     * Notifies when a Buffer is ready to allocate from Graphics.
     * If generation does not match to the current, notifications via the interface
     * will be ignored. (In the case, the notifications are from one of the old surfaces
     * which is no longer used.)
     *
     * @param[in] generation    generation id for specifying Graphics(BQ)
     */
    void onReleased(uint32_t generation);

    /**
     * Get waitable fds for events.(allocate is ready, end of life cycle)
     *
     * @param[out]  allocFd     eventFd which signals being ready to allocate
     * @param[out]  statusFd    eventFd which signals end of life cycle.
     *                          When signaled no more allocate is possible.
     * @return  C2_OK
     *          C2_NO_MEMORY    Max # of fd reached.(not really a memory issue)
     */
    c2_status_t getWaitableFds(int *allocFd, int *statusFd);

    /**
     *  Ends to use the class. after the call, allocate will fail.
     */
    void stop();

private:
    static constexpr int kDefaultMaxDequeue = 2;

    struct BufferCache;

    struct BufferItem {
        bool mInit;
        uint64_t mId;
        uint32_t mGeneration;
        int mSlot;
        AHardwareBuffer *mBuf;
        uint64_t mUsage; // Gralloc usage format, not AHB
        sp<Fence> mFence;

        // Create from a GraphicBuffer
        BufferItem(uint32_t generation, int slot,
                   const sp<GraphicBuffer>& buf,
                   const sp<Fence> &fence);

        // Create from an AHB (no slot information)
        // Should be attached to IGBP for rendering
        BufferItem(uint32_t generation,
                   AHardwareBuffer_Desc *desc,
                   AHardwareBuffer *pBuf);

        ~BufferItem();

        sp<GraphicBuffer> updateBuffer(uint64_t newUsage, uint32_t newGeneration);
    };

    struct BufferCache {
        static constexpr int kNumSlots = ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;

        uint64_t mBqId;
        uint32_t mGeneration;
        ::android::sp<IGraphicBufferProducer> mIgbp;

        // Maps slotId to buffer
        // IGBP::dequeueBuffer(), IGBP::queueBuffer() and IGBP::cancelBuffer()
        // require slotId.
        std::map<int, std::shared_ptr<BufferItem>> mBuffers;

        // block slot use, while deallocating(cancel, render and etc)
        struct BlockedSlot {
            std::mutex l;
            std::condition_variable cv;
            bool blocked;
            BlockedSlot() : blocked{false} {}
            ~BlockedSlot() = default;
        };

        BlockedSlot mBlockedSlots[kNumSlots];

        BufferCache() : mBqId{0ULL}, mGeneration{0}, mIgbp{nullptr} {}
        BufferCache(uint64_t bqId, uint32_t generation, const sp<IGraphicBufferProducer>& igbp) :
            mBqId{bqId}, mGeneration{generation}, mIgbp{igbp} {}

        void waitOnSlot(int slot);

        void blockSlot(int slot);

        void unblockSlot(int slot);
    };

    std::shared_ptr<BufferCache> mBufferCache;
    // Maps bufferId to buffer
    std::map<uint64_t, std::shared_ptr<BufferItem>> mDequeued;
    std::set<uint64_t> mDeallocating;

    int mMaxDequeue;
    int mMaxDequeueRequested;
    int mMaxDequeueCommitted;

    uint32_t mMaxDequeueRequestedSeqId;
    uint32_t mMaxDequeueCommittedSeqId;

    int mDequeueable;

    // TODO: statistics
    uint64_t mTotalDequeued;
    //uint64_t mTotalQueued;
    uint64_t mTotalCancelled;
    uint64_t mTotalDropped;
    uint64_t mTotalReleased;

    bool mInConfig;
    std::mutex mLock; // locks for data synchronization
    std::mutex mConfigLock; // locks for configuration change.

    std::atomic<bool> mStopped;

    ::android::base::unique_fd mAllocEventFd; // eventfd in semaphore mode which
                                              // mirrors mDqueueable.
    ::android::base::unique_fd mStopEventFd; // eventfd which indicates the life
                                             // cycle of the class being stopped.

    std::thread mEventQueueThread; // Thread to handle interrupted
                                   // writes to eventfd{s}.
    std::mutex mEventLock;
    std::condition_variable mEventCv;

    bool mStopEventThread;
    int mIncDequeueable; // pending # of write to increase dequeueable eventfd
    bool mStopRequest; // pending write to statusfd

private:
    explicit GraphicsTracker(int maxDequeueCount);

    // return {@code true} only when dequeue config adjust happened.
    // {@code updateDequeueConf} is an output parameter, and returns
    // {@code true} only when the current dequeue conf is required to be
    // updated to IGBP(BQ) as a result of the adjust.
    bool adjustDequeueConfLocked(bool *updateDequeueConf);

    void updateDequeueConf();

    c2_status_t requestAllocate(std::shared_ptr<BufferCache> *cache);
    c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
                                  bool *completed, bool *updateDequeue,
                                  std::shared_ptr<BufferCache> *cache, int *slotId,
                                  sp<Fence> *rFence);
    c2_status_t requestRender(uint64_t bid, std::shared_ptr<BufferCache> *cache,
                              std::shared_ptr<BufferItem> *pBuffer,
                              bool *updateDequeue);

    void commitAllocate(c2_status_t res,
                        const std::shared_ptr<BufferCache> &cache,
                        bool cached, int slotId, const sp<Fence> &fence,
                        std::shared_ptr<BufferItem> *buffer,
                        bool *updateDequeue);
    void commitDeallocate(std::shared_ptr<BufferCache> &cache, int slotId, uint64_t bid);
    void commitRender(uint64_t origBid,
                      const std::shared_ptr<BufferCache> &cache,
                      const std::shared_ptr<BufferItem> &buffer,
                      bool *updateDequeue);

    c2_status_t _allocate(
            const std::shared_ptr<BufferCache> &cache,
            uint32_t width, uint32_t height, PixelFormat format, int64_t usage,
            bool *cached, int *rSlotId, sp<Fence> *rFence,
            std::shared_ptr<BufferItem> *buffer);

    void writeIncDequeueable(int inc);
    void processEvent();
};

} // namespace aidl::android::hardware::media::c2::implementation