xref: /aosp_15_r20/frameworks/av/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <android/hardware_buffer.h>
20 #include <android-base/unique_fd.h>
21 #include <gui/IGraphicBufferProducer.h>
22 
23 #include <atomic>
24 #include <condition_variable>
25 #include <map>
26 #include <memory>
27 #include <mutex>
28 #include <set>
29 #include <thread>
30 #include <optional>
31 
32 #include <C2Buffer.h>
33 
34 namespace aidl::android::hardware::media::c2::implementation {
35 
36 using ::android::IGraphicBufferProducer;
37 using ::android::GraphicBuffer;
38 using ::android::FrameEventHistoryDelta;
39 using ::android::Fence;
40 using ::android::PixelFormat;
41 using ::android::sp;
42 /**
43  * The class allocates AHardwareBuffer(GraphicBuffer)s using BufferQueue.
44  *
45  * The class tracks and manages outstanding # of allocations for buffer
46  * recycling. So Graphics operations which affects # of outstanding allocation
47  * should be done via the class. (e.g. rendering a buffer to display)
48  *
49  * The class is supposed to be wrapped into IGraphicBufferAllocator AIDL interface,
50  * and the interface will be passed to HAL for a specific BlockPool instance.
51  *
52  * The class has one to one relation with HAL side Graphic C2BlockPool.
53  * The life cycle of the class is tied to a HAL side BlockPool object.
54  *
55  * So, reset()/stop() of HAL which related to blokcpool destruction will terminate the
56  * use of the class. And a new instance should be created in order for start()
57  * of HAL.
58  */
59 class GraphicsTracker {
60 public:
CreateGraphicsTracker(int maxDequeueCount)61     static std::shared_ptr<GraphicsTracker> CreateGraphicsTracker(int maxDequeueCount) {
62         GraphicsTracker *p = new GraphicsTracker(maxDequeueCount);
63         std::shared_ptr<GraphicsTracker> sp(p);
64         return sp;
65     }
66 
67     ~GraphicsTracker();
68 
69     /**
70      * Configure a new surface to render/allocate graphic blocks.
71      *
72      * Graphic  blocks from the old surface will be migrated to the new surface,
73      * if possible. Configuring to a null surface is possible in the case,
74      * an allocation request will be fulfilled by a direct allocation(not using
75      * BQ). generation should be different to the previous generations.
76      *
77      * @param[in] igbp        the new surface to configure
78      * @param[in] generation  identifier for each configured surface
79      */
80     c2_status_t configureGraphics(const sp<IGraphicBufferProducer>& igbp, uint32_t generation);
81 
82     /**
83      * Configure max # of outstanding allocations at any given time.
84      *
85      * @param[in] maxDequeueCount    max # of outstanding allocation to configure
86      */
87     c2_status_t configureMaxDequeueCount(int maxDequeueCount);
88 
89     /**
90      * Allocates a AHardwareBuffer.
91      *
92      * @param[in] width       width
93      * @param[in] height      height
94      * @param[in] PixelFormat pixel format which describes color format and etc
95      * @param[in] usage       gralloc usage bits
96      * @param[out] buf        the allocated buffer
97      * @param[out] fence      fence for the allocated buffer
98      * @return  C2_OK         the buffer is allocated
99      *          C2_BAD_STATE  stop() is called and in stopped state
100      *          C2_BLOCKING   should be waited to allocate
101      *          C2_NO_MEMORY  out of memory
102      *          C2_CORRUPTED
103      */
104     c2_status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
105                          AHardwareBuffer **buf, sp<Fence> *fence);
106 
107     /**
108      * Deallocates a AHardwareBuffer
109      *
110      * @param[in] bufId         id of the buffer to deallocate
111      * @param[in] fence         i/o fence for the buffer
112      * @return  C2_OK           the buffer is successfully deallocated.
113      *          C2_DUPLICATE    deallocation/render request is pending already.
114      *          C2_NOT_FOUND    the buffer with the id is not allocated.
115      */
116     c2_status_t deallocate(uint64_t bufId, const sp<Fence> &fence);
117 
118     /**
119      * Render a GraphicBlock which is associated to a pending allocated buffer
120      *
121      * @param[in] block         GraphicBlock
122      * @param[in] input         render input params to Graphics
123      * @param[out] output       render output params from Graphics
124      * @return  C2_OK           the buffer is now ready to render
125      *          C2_BAD_STATE    there is no surface to render.
126      *                          (null surface mode or life cycle ends)
127      *          C2_DUPLICATE    deallocation/render request is pending already.
128      *          C2_NOT_FOUND    the buffer with the id is not allocated.
129      *          C2_REFUSED      the buffer is refused to render from Graphics
130      *          C2_CORRUPTED
131      */
132     c2_status_t render(const C2ConstGraphicBlock& block,
133                        const IGraphicBufferProducer::QueueBufferInput& input,
134                        IGraphicBufferProducer::QueueBufferOutput *output);
135 
136     /**
137      * Retrieve frame event history from the crurrent surface if any.
138      */
139     void pollForRenderedFrames(FrameEventHistoryDelta* delta);
140 
141     /**
142      * Notifies when a Buffer is ready to allocate from Graphics.
143      * If generation does not match to the current, notifications via the interface
144      * will be ignored. (In the case, the notifications are from one of the old surfaces
145      * which is no longer used.)
146      *
147      * @param[in] generation    generation id for specifying Graphics(BQ)
148      */
149     void onReleased(uint32_t generation);
150 
151     /**
152      * Notifies when a Buffer is attached to Graphics(consumer side).
153      * If generation does not match to the current, notifications via the interface
154      * will be ignored. (In the case, the notifications are from one of the old surfaces
155      * which is no longer used.)
156      * One onReleased() should be ignored for one onAttached() when both of
157      * them have the same generation as params.
158      *
159      * @param[in] generation    generation id for specifying Graphics(BQ)
160      */
161     void onAttached(uint32_t generation);
162 
163     /**
164      * Get waitable fd for events.(allocate is ready, end of life cycle)
165      *
166      * @param[out]  pipeFd      a file descriptor created from pipe2()
167      *                          in order for notifying being ready to allocate
168      *
169      * @return  C2_OK
170      *          C2_NO_MEMORY    Max # of fd reached.(not really a memory issue)
171      */
172     c2_status_t getWaitableFd(int *pipeFd);
173 
174     /**
175      * Get the current max allocatable/dequeueable buffer count without de-allocating.
176      */
177     int getCurDequeueable();
178 
179     /**
180      *  Ends to use the class. after the call, allocate will fail.
181      */
182     void stop();
183 
184     /**
185      * stop()/release() request to HAL is in process from the client.
186      * The class will never be active again after the request.
187      * Still, allocation requests from HAL should be served until stop()
188      * is being called.
189      */
190     void onRequestStop();
191 
192 private:
193     struct BufferCache;
194 
195     struct BufferItem {
196         bool mInit;
197         uint64_t mId;
198         uint32_t mGeneration;
199         int mSlot;
200         AHardwareBuffer *mBuf;
201         uint64_t mUsage; // Gralloc usage format, not AHB
202         sp<Fence> mFence;
203 
204         // Create from a GraphicBuffer
205         BufferItem(uint32_t generation, int slot,
206                    const sp<GraphicBuffer>& buf,
207                    const sp<Fence> &fence);
208 
209         // Create from an AHB (no slot information)
210         // Should be attached to IGBP for rendering
211         BufferItem(uint32_t generation,
212                    AHardwareBuffer *pBuf,
213                    uint64_t usage);
214 
215         ~BufferItem();
216 
217         std::shared_ptr<BufferItem> migrateBuffer(uint64_t newUsage, uint32_t newGeneration);
218 
219         sp<GraphicBuffer> getGraphicBuffer();
220 
221     };
222 
223     struct BufferCache {
224         static constexpr int kNumSlots = ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
225 
226         uint64_t mBqId;
227         uint32_t mGeneration;
228         ::android::sp<IGraphicBufferProducer> mIgbp;
229 
230         // Maps slotId to buffer
231         // IGBP::dequeueBuffer(), IGBP::queueBuffer() and IGBP::cancelBuffer()
232         // require slotId.
233         std::map<int, std::shared_ptr<BufferItem>> mBuffers;
234 
235         // block slot use, while deallocating(cancel, render and etc)
236         struct BlockedSlot {
237             std::mutex l;
238             std::condition_variable cv;
239             bool blocked;
BlockedSlotBufferCache::BlockedSlot240             BlockedSlot() : blocked{false} {}
241             ~BlockedSlot() = default;
242         };
243 
244         BlockedSlot mBlockedSlots[kNumSlots];
245 
246         std::atomic<int> mNumAttached;
247 
BufferCacheBufferCache248         BufferCache() : mBqId{0ULL}, mGeneration{0}, mIgbp{nullptr}, mNumAttached{0} {}
BufferCacheBufferCache249         BufferCache(uint64_t bqId, uint32_t generation, const sp<IGraphicBufferProducer>& igbp) :
250             mBqId{bqId}, mGeneration{generation}, mIgbp{igbp}, mNumAttached{0} {}
251 
252         ~BufferCache();
253 
254         void waitOnSlot(int slot);
255 
256         void blockSlot(int slot);
257 
258         void unblockSlot(int slot);
259     };
260 
261     std::shared_ptr<BufferCache> mBufferCache;
262     // Maps bufferId to buffer
263     std::map<uint64_t, std::shared_ptr<BufferItem>> mDequeued;
264     std::set<uint64_t> mDeallocating;
265     int mNumDequeueing;
266 
267     // These member variables are read and modified accessed as follows.
268     // 1. mConfigLock being held
269     //    Set mInConfig true with mLock in the beginning
270     //    Clear mInConfig with mLock in the end
271     // 2. mLock is held and mInConfig is false.
272     int mMaxDequeue;
273     int mMaxDequeueCommitted;
274     std::optional<int> mMaxDequeueRequested;
275 
276     int mDequeueable;
277 
278     // TODO: statistics
279     uint64_t mTotalDequeued;
280     //uint64_t mTotalQueued;
281     uint64_t mTotalCancelled;
282     uint64_t mTotalDropped;
283     uint64_t mTotalReleased;
284 
285     bool mInConfig;
286     std::mutex mLock; // locks for data synchronization
287     std::mutex mConfigLock; // locks for configuration change.
288 
289     // NOTE: pipe2() creates two file descriptors for allocatable events
290     // and irrecoverable error events notification.
291     //
292     // A byte will be written to the writing end whenever a buffer is ready to
293     // dequeue/allocate. A byte will be read from the reading end whenever
294     // an allocate/dequeue event happens.
295     //
296     // The writing end will be closed when the end-of-lifecycle event was met.
297     //
298     // The reading end will be shared to the remote processes. Remote processes
299     // use ::poll() to check whether a buffer is ready to allocate/ready.
300     // Also ::poll() will let remote processes know the end-of-lifecycle event
301     // by returning POLLHUP event from the reading end.
302     ::android::base::unique_fd mReadPipeFd;   // The reading end file descriptor
303     ::android::base::unique_fd mWritePipeFd;  // The writing end file descriptor
304 
305     std::atomic<bool> mStopped;
306 
307     bool mStopRequested;
308     std::atomic<int> mAllocAfterStopRequested;
309 
310 
311 private:
312     explicit GraphicsTracker(int maxDequeueCount);
313 
314     // return {@code true} only when dequeue config adjust happened.
315     // {@code updateDequeueConf} is an output parameter, and returns
316     // {@code true} only when the current dequeue conf is required to be
317     // updated to IGBP(BQ) as a result of the adjust.
318     bool adjustDequeueConfLocked(bool *updateDequeueConf);
319 
320     void updateDequeueConf();
321     void clearCacheIfNecessaryLocked(
322             const std::shared_ptr<BufferCache> &cache,
323             int maxDequeueCommitted);
324 
325     c2_status_t requestAllocateLocked(std::shared_ptr<BufferCache> *cache);
326     c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
327                                   bool *completed, bool *updateDequeue,
328                                   std::shared_ptr<BufferCache> *cache, int *slotId,
329                                   sp<Fence> *rFence);
330     c2_status_t requestRender(uint64_t bid, std::shared_ptr<BufferCache> *cache,
331                               std::shared_ptr<BufferItem> *pBuffer,
332                               bool *fromCache,
333                               bool *updateDequeue);
334 
335     void commitAllocate(c2_status_t res,
336                         const std::shared_ptr<BufferCache> &cache,
337                         bool cached, int slotId, const sp<Fence> &fence,
338                         std::shared_ptr<BufferItem> *buffer,
339                         bool *updateDequeue);
340     void commitDeallocate(std::shared_ptr<BufferCache> &cache,
341                           int slotId, uint64_t bid,
342                           bool *updateDequeue);
343     void commitRender(const std::shared_ptr<BufferCache> &cache,
344                       const std::shared_ptr<BufferItem> &buffer,
345                       const std::shared_ptr<BufferItem> &oldBuffer,
346                       bool bufferReplaced,
347                       bool *updateDequeue);
348 
349     c2_status_t _allocate(
350             const std::shared_ptr<BufferCache> &cache,
351             uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
352             bool *cached, int *rSlotId, sp<Fence> *rFence,
353             std::shared_ptr<BufferItem> *buffer);
354 
355     c2_status_t _allocateDirect(
356             uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
357             AHardwareBuffer **buf, sp<Fence> *fence);
358 
359     void writeIncDequeueableLocked(int inc);
360     void drainDequeueableLocked(int dec);
361 };
362 
363 } // namespace aidl::android::hardware::media::c2::implementation
364