• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         uint64_t mUsage;
228         uint32_t mGeneration;
229         ::android::sp<IGraphicBufferProducer> mIgbp;
230 
231         // Maps slotId to buffer
232         // IGBP::dequeueBuffer(), IGBP::queueBuffer() and IGBP::cancelBuffer()
233         // require slotId.
234         std::map<int, std::shared_ptr<BufferItem>> mBuffers;
235 
236         // block slot use, while deallocating(cancel, render and etc)
237         struct BlockedSlot {
238             std::mutex l;
239             std::condition_variable cv;
240             bool blocked;
BlockedSlotBufferCache::BlockedSlot241             BlockedSlot() : blocked{false} {}
242             ~BlockedSlot() = default;
243         };
244 
245         BlockedSlot mBlockedSlots[kNumSlots];
246 
247         std::atomic<int> mNumAttached;
248 
BufferCacheBufferCache249         BufferCache() : mBqId{0ULL}, mUsage{0ULL},
250                 mGeneration{0}, mIgbp{nullptr}, mNumAttached{0} {}
BufferCacheBufferCache251         BufferCache(uint64_t bqId, uint64_t usage, uint32_t generation,
252                 const sp<IGraphicBufferProducer>& igbp) :
253             mBqId{bqId}, mUsage{usage}, mGeneration{generation}, mIgbp{igbp}, mNumAttached{0} {}
254 
255         ~BufferCache();
256 
257         void waitOnSlot(int slot);
258 
259         void blockSlot(int slot);
260 
261         void unblockSlot(int slot);
262     };
263 
264     std::shared_ptr<BufferCache> mBufferCache;
265     // Maps bufferId to buffer
266     std::map<uint64_t, std::shared_ptr<BufferItem>> mDequeued;
267     std::set<uint64_t> mDeallocating;
268     int mNumDequeueing;
269 
270     // These member variables are read and modified accessed as follows.
271     // 1. mConfigLock being held
272     //    Set mInConfig true with mLock in the beginning
273     //    Clear mInConfig with mLock in the end
274     // 2. mLock is held and mInConfig is false.
275     int mMaxDequeue;
276     int mMaxDequeueCommitted;
277     std::optional<int> mMaxDequeueRequested;
278 
279     int mDequeueable;
280 
281     // TODO: statistics
282     uint64_t mTotalDequeued;
283     //uint64_t mTotalQueued;
284     uint64_t mTotalCancelled;
285     uint64_t mTotalDropped;
286     uint64_t mTotalReleased;
287 
288     bool mInConfig;
289     std::mutex mLock; // locks for data synchronization
290     std::mutex mConfigLock; // locks for configuration change.
291 
292     // NOTE: pipe2() creates two file descriptors for allocatable events
293     // and irrecoverable error events notification.
294     //
295     // A byte will be written to the writing end whenever a buffer is ready to
296     // dequeue/allocate. A byte will be read from the reading end whenever
297     // an allocate/dequeue event happens.
298     //
299     // The writing end will be closed when the end-of-lifecycle event was met.
300     //
301     // The reading end will be shared to the remote processes. Remote processes
302     // use ::poll() to check whether a buffer is ready to allocate/ready.
303     // Also ::poll() will let remote processes know the end-of-lifecycle event
304     // by returning POLLHUP event from the reading end.
305     ::android::base::unique_fd mReadPipeFd;   // The reading end file descriptor
306     ::android::base::unique_fd mWritePipeFd;  // The writing end file descriptor
307 
308     std::atomic<bool> mStopped;
309 
310     bool mStopRequested;
311     std::atomic<int> mAllocAfterStopRequested;
312 
313     // Release Surface where we get allocations after stop/release being requested.
314     class PlaceHolderSurface;
315     std::unique_ptr<PlaceHolderSurface> mReleaseSurface;
316 
317 
318 private:
319     explicit GraphicsTracker(int maxDequeueCount);
320 
321     // return {@code true} only when dequeue config adjust happened.
322     // {@code updateDequeueConf} is an output parameter, and returns
323     // {@code true} only when the current dequeue conf is required to be
324     // updated to IGBP(BQ) as a result of the adjust.
325     bool adjustDequeueConfLocked(bool *updateDequeueConf);
326 
327     void updateDequeueConf();
328     void clearCacheIfNecessaryLocked(
329             const std::shared_ptr<BufferCache> &cache,
330             int maxDequeueCommitted);
331 
332     c2_status_t requestAllocateLocked(std::shared_ptr<BufferCache> *cache);
333     c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
334                                   bool *completed, bool *updateDequeue,
335                                   std::shared_ptr<BufferCache> *cache, int *slotId,
336                                   sp<Fence> *rFence);
337     c2_status_t requestRender(uint64_t bid, std::shared_ptr<BufferCache> *cache,
338                               std::shared_ptr<BufferItem> *pBuffer,
339                               bool *fromCache,
340                               bool *updateDequeue);
341 
342     void commitAllocate(c2_status_t res,
343                         const std::shared_ptr<BufferCache> &cache,
344                         bool cached, int slotId, const sp<Fence> &fence,
345                         std::shared_ptr<BufferItem> *buffer,
346                         bool *updateDequeue);
347     void commitDeallocate(std::shared_ptr<BufferCache> &cache,
348                           int slotId, uint64_t bid,
349                           bool *updateDequeue);
350     void commitRender(const std::shared_ptr<BufferCache> &cache,
351                       const std::shared_ptr<BufferItem> &buffer,
352                       const std::shared_ptr<BufferItem> &oldBuffer,
353                       bool bufferReplaced,
354                       bool *updateDequeue);
355 
356     c2_status_t _allocate(
357             const std::shared_ptr<BufferCache> &cache,
358             uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
359             bool *cached, int *rSlotId, sp<Fence> *rFence,
360             std::shared_ptr<BufferItem> *buffer);
361 
362     c2_status_t _allocateDirect(
363             uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
364             AHardwareBuffer **buf, sp<Fence> *fence);
365 
366     void writeIncDequeueableLocked(int inc);
367     void drainDequeueableLocked(int dec);
368 };
369 
370 } // namespace aidl::android::hardware::media::c2::implementation
371