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