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