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::Fence;
39 using ::android::PixelFormat;
40 using ::android::sp;
41 /**
42  * The class allocates AHardwareBuffer(GraphicBuffer)s using BufferQueue.
43  *
44  * The class tracks and manages outstanding # of allocations for buffer
45  * recycling. So Graphics operations which affects # of outstanding allocation
46  * should be done via the class. (e.g. rendering a buffer to display)
47  *
48  * The class is supposed to be wrapped into IGraphicBufferAllocator AIDL interface,
49  * and the interface will be passed to HAL for a specific BlockPool instance.
50  *
51  * The class has one to one relation with HAL side Graphic C2BlockPool.
52  * The life cycle of the class is tied to a HAL side BlockPool object.
53  *
54  * So, reset()/stop() of HAL which related to blokcpool destruction will terminate the
55  * use of the class. And a new instance should be created in order for start()
56  * of HAL.
57  */
58 class GraphicsTracker {
59 public:
CreateGraphicsTracker(int maxDequeueCount)60     static std::shared_ptr<GraphicsTracker> CreateGraphicsTracker(int maxDequeueCount) {
61         GraphicsTracker *p = new GraphicsTracker(maxDequeueCount);
62         std::shared_ptr<GraphicsTracker> sp(p);
63         return sp;
64     }
65 
66     ~GraphicsTracker();
67 
68     /**
69      * Configure a new surface to render/allocate graphic blocks.
70      *
71      * Graphic  blocks from the old surface will be migrated to the new surface,
72      * if possible. Configuring to a null surface is possible in the case,
73      * an allocation request will be fulfilled by a direct allocation(not using
74      * BQ). generation should be different to the previous generations.
75      *
76      * @param[in] igbp        the new surface to configure
77      * @param[in] generation  identifier for each configured surface
78      */
79     c2_status_t configureGraphics(const sp<IGraphicBufferProducer>& igbp, uint32_t generation);
80 
81     /**
82      * Configure max # of outstanding allocations at any given time.
83      *
84      * @param[in] maxDequeueCount    max # of outstanding allocation to configure
85      */
86     c2_status_t configureMaxDequeueCount(int maxDequeueCount);
87 
88     /**
89      * Allocates a AHardwareBuffer.
90      *
91      * @param[in] width       width
92      * @param[in] height      height
93      * @param[in] PixelFormat pixel format which describes color format and etc
94      * @param[in] usage       gralloc usage bits
95      * @param[out] buf        the allocated buffer
96      * @param[out] fence      fence for the allocated buffer
97      * @return  C2_OK         the buffer is allocated
98      *          C2_BAD_STATE  stop() is called and in stopped state
99      *          C2_BLOCKING   should be waited to allocate
100      *          C2_NO_MEMORY  out of memory
101      *          C2_CORRUPTED
102      */
103     c2_status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
104                          AHardwareBuffer **buf, sp<Fence> *fence);
105 
106     /**
107      * Deallocates a AHardwareBuffer
108      *
109      * @param[in] bufId         id of the buffer to deallocate
110      * @param[in] fence         i/o fence for the buffer
111      * @return  C2_OK           the buffer is successfully deallocated.
112      *          C2_DUPLICATE    deallocation/render request is pending already.
113      *          C2_NOT_FOUND    the buffer with the id is not allocated.
114      */
115     c2_status_t deallocate(uint64_t bufId, const sp<Fence> &fence);
116 
117     /**
118      * Render a GraphicBlock which is associated to a pending allocated buffer
119      *
120      * @param[in] block         GraphicBlock
121      * @param[in] input         render input params to Graphics
122      * @param[out] output       render output params from Graphics
123      * @return  C2_OK           the buffer is now ready to render
124      *          C2_BAD_STATE    there is no surface to render.
125      *                          (null surface mode or life cycle ends)
126      *          C2_DUPLICATE    deallocation/render request is pending already.
127      *          C2_NOT_FOUND    the buffer with the id is not allocated.
128      *          C2_REFUSED      the buffer is refused to render from Graphics
129      *          C2_CORRUPTED
130      */
131     c2_status_t render(const C2ConstGraphicBlock& block,
132                        const IGraphicBufferProducer::QueueBufferInput& input,
133                        IGraphicBufferProducer::QueueBufferOutput *output);
134 
135     /**
136      * Notifies when a Buffer is ready to allocate from Graphics.
137      * If generation does not match to the current, notifications via the interface
138      * will be ignored. (In the case, the notifications are from one of the old surfaces
139      * which is no longer used.)
140      *
141      * @param[in] generation    generation id for specifying Graphics(BQ)
142      */
143     void onReleased(uint32_t generation);
144 
145     /**
146      * Get waitable fd for events.(allocate is ready, end of life cycle)
147      *
148      * @param[out]  pipeFd      a file descriptor created from pipe2()
149      *                          in order for notifying being ready to allocate
150      *
151      * @return  C2_OK
152      *          C2_NO_MEMORY    Max # of fd reached.(not really a memory issue)
153      */
154     c2_status_t getWaitableFd(int *pipeFd);
155 
156     /**
157      * Get the current max allocatable/dequeueable buffer count without de-allocating.
158      */
159     int getCurDequeueable();
160 
161     /**
162      *  Ends to use the class. after the call, allocate will fail.
163      */
164     void stop();
165 
166 private:
167     struct BufferCache;
168 
169     struct BufferItem {
170         bool mInit;
171         uint64_t mId;
172         uint32_t mGeneration;
173         int mSlot;
174         AHardwareBuffer *mBuf;
175         uint64_t mUsage; // Gralloc usage format, not AHB
176         sp<Fence> mFence;
177 
178         // Create from a GraphicBuffer
179         BufferItem(uint32_t generation, int slot,
180                    const sp<GraphicBuffer>& buf,
181                    const sp<Fence> &fence);
182 
183         // Create from an AHB (no slot information)
184         // Should be attached to IGBP for rendering
185         BufferItem(uint32_t generation,
186                    AHardwareBuffer *pBuf,
187                    uint64_t usage);
188 
189         ~BufferItem();
190 
191         std::shared_ptr<BufferItem> migrateBuffer(uint64_t newUsage, uint32_t newGeneration);
192 
193         sp<GraphicBuffer> getGraphicBuffer();
194 
195     };
196 
197     struct BufferCache {
198         static constexpr int kNumSlots = ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
199 
200         uint64_t mBqId;
201         uint32_t mGeneration;
202         ::android::sp<IGraphicBufferProducer> mIgbp;
203 
204         // Maps slotId to buffer
205         // IGBP::dequeueBuffer(), IGBP::queueBuffer() and IGBP::cancelBuffer()
206         // require slotId.
207         std::map<int, std::shared_ptr<BufferItem>> mBuffers;
208 
209         // block slot use, while deallocating(cancel, render and etc)
210         struct BlockedSlot {
211             std::mutex l;
212             std::condition_variable cv;
213             bool blocked;
BlockedSlotBufferCache::BlockedSlot214             BlockedSlot() : blocked{false} {}
215             ~BlockedSlot() = default;
216         };
217 
218         BlockedSlot mBlockedSlots[kNumSlots];
219 
BufferCacheBufferCache220         BufferCache() : mBqId{0ULL}, mGeneration{0}, mIgbp{nullptr} {}
BufferCacheBufferCache221         BufferCache(uint64_t bqId, uint32_t generation, const sp<IGraphicBufferProducer>& igbp) :
222             mBqId{bqId}, mGeneration{generation}, mIgbp{igbp} {}
223 
224         ~BufferCache();
225 
226         void waitOnSlot(int slot);
227 
228         void blockSlot(int slot);
229 
230         void unblockSlot(int slot);
231     };
232 
233     std::shared_ptr<BufferCache> mBufferCache;
234     // Maps bufferId to buffer
235     std::map<uint64_t, std::shared_ptr<BufferItem>> mDequeued;
236     std::set<uint64_t> mDeallocating;
237     int mNumDequeueing;
238 
239     // These member variables are read and modified accessed as follows.
240     // 1. mConfigLock being held
241     //    Set mInConfig true with mLock in the beginning
242     //    Clear mInConfig with mLock in the end
243     // 2. mLock is held and mInConfig is false.
244     int mMaxDequeue;
245     int mMaxDequeueCommitted;
246     std::optional<int> mMaxDequeueRequested;
247 
248     int mDequeueable;
249 
250     // TODO: statistics
251     uint64_t mTotalDequeued;
252     //uint64_t mTotalQueued;
253     uint64_t mTotalCancelled;
254     uint64_t mTotalDropped;
255     uint64_t mTotalReleased;
256 
257     bool mInConfig;
258     std::mutex mLock; // locks for data synchronization
259     std::mutex mConfigLock; // locks for configuration change.
260 
261     // NOTE: pipe2() creates two file descriptors for allocatable events
262     // and irrecoverable error events notification.
263     //
264     // A byte will be written to the writing end whenever a buffer is ready to
265     // dequeue/allocate. A byte will be read from the reading end whenever
266     // an allocate/dequeue event happens.
267     //
268     // The writing end will be closed when the end-of-lifecycle event was met.
269     //
270     // The reading end will be shared to the remote processes. Remote processes
271     // use ::poll() to check whether a buffer is ready to allocate/ready.
272     // Also ::poll() will let remote processes know the end-of-lifecycle event
273     // by returning POLLHUP event from the reading end.
274     ::android::base::unique_fd mReadPipeFd;   // The reading end file descriptor
275     ::android::base::unique_fd mWritePipeFd;  // The writing end file descriptor
276 
277     std::atomic<bool> mStopped;
278 
279 private:
280     explicit GraphicsTracker(int maxDequeueCount);
281 
282     // return {@code true} only when dequeue config adjust happened.
283     // {@code updateDequeueConf} is an output parameter, and returns
284     // {@code true} only when the current dequeue conf is required to be
285     // updated to IGBP(BQ) as a result of the adjust.
286     bool adjustDequeueConfLocked(bool *updateDequeueConf);
287 
288     void updateDequeueConf();
289     void clearCacheIfNecessaryLocked(
290             const std::shared_ptr<BufferCache> &cache,
291             int maxDequeueCommitted);
292 
293     c2_status_t requestAllocate(std::shared_ptr<BufferCache> *cache);
294     c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
295                                   bool *completed, bool *updateDequeue,
296                                   std::shared_ptr<BufferCache> *cache, int *slotId,
297                                   sp<Fence> *rFence);
298     c2_status_t requestRender(uint64_t bid, std::shared_ptr<BufferCache> *cache,
299                               std::shared_ptr<BufferItem> *pBuffer,
300                               bool *fromCache,
301                               bool *updateDequeue);
302 
303     void commitAllocate(c2_status_t res,
304                         const std::shared_ptr<BufferCache> &cache,
305                         bool cached, int slotId, const sp<Fence> &fence,
306                         std::shared_ptr<BufferItem> *buffer,
307                         bool *updateDequeue);
308     void commitDeallocate(std::shared_ptr<BufferCache> &cache,
309                           int slotId, uint64_t bid,
310                           bool *updateDequeue);
311     void commitRender(const std::shared_ptr<BufferCache> &cache,
312                       const std::shared_ptr<BufferItem> &buffer,
313                       const std::shared_ptr<BufferItem> &oldBuffer,
314                       bool bufferReplaced,
315                       bool *updateDequeue);
316 
317     c2_status_t _allocate(
318             const std::shared_ptr<BufferCache> &cache,
319             uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
320             bool *cached, int *rSlotId, sp<Fence> *rFence,
321             std::shared_ptr<BufferItem> *buffer);
322 
323     void writeIncDequeueableLocked(int inc);
324     void drainDequeueableLocked(int dec);
325 };
326 
327 } // namespace aidl::android::hardware::media::c2::implementation
328