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