1 /*############################################################################ 2 # Copyright (C) 2005 Intel Corporation 3 # 4 # SPDX-License-Identifier: MIT 5 ############################################################################*/ 6 7 #ifndef __MFX_BUFFERING_H__ 8 #define __MFX_BUFFERING_H__ 9 10 #include <stdio.h> 11 #include <mutex> 12 13 #include "vpl/mfxstructures.h" 14 15 #include "vm/atomic_defs.h" 16 #include "vm/strings_defs.h" 17 #include "vm/thread_defs.h" 18 #include "vm/time_defs.h" 19 20 struct msdkFrameSurface { 21 mfxFrameSurface1 22 frame; // NOTE: this _should_ be the first item (see CBuffering::FindUsedSurface()) 23 msdk_tick submit; // tick when frame was submitted for processing 24 mfxU16 render_lock; // signifies that frame is locked for rendering 25 msdkFrameSurface* prev; 26 msdkFrameSurface* next; 27 }; 28 29 struct msdkOutputSurface { 30 msdkFrameSurface* surface; 31 mfxSyncPoint syncp; 32 msdkOutputSurface* next; 33 }; 34 35 /** \brief Debug purpose macro to terminate execution if buggy situation happenned. 36 * 37 * Use this macro to check impossible, buggy condition which should not occur under 38 * normal circumstances. Macro should be used where check in release mode is not 39 * desirable and atually needed. 40 */ 41 #if 0 //_DEBUG 42 #define MSDK_SELF_CHECK(C) \ 43 { \ 44 if (!(C)) { \ 45 msdk_printf(MSDK_STRING("bug: %s:%d: self-check failed: '%s' is not true\n"), \ 46 __FILE__, \ 47 __LINE__, \ 48 #C); \ 49 exit(-1); \ 50 } \ 51 } 52 #else 53 #define MSDK_SELF_CHECK(C) 54 #endif 55 56 class CBuffering; 57 58 // LIFO list of frame surfaces 59 class msdkFreeSurfacesPool { 60 friend class CBuffering; 61 62 public: msdkFreeSurfacesPool(std::mutex & mutex)63 msdkFreeSurfacesPool(std::mutex& mutex) : m_pSurfaces(NULL), m_rMutex(mutex) {} 64 ~msdkFreeSurfacesPool()65 ~msdkFreeSurfacesPool() { 66 m_pSurfaces = NULL; 67 } 68 /** \brief The function adds free surface to the free surfaces array. 69 * 70 * @note That's caller responsibility to pass valid surface. 71 * @note We always add and get free surface from the array head. In case not all surfaces 72 * will be actually used we have good chance to avoid actual allocation of the surface memory. 73 */ AddSurface(msdkFrameSurface * surface)74 inline void AddSurface(msdkFrameSurface* surface) { 75 std::lock_guard<std::mutex> lock(m_rMutex); 76 AddSurfaceUnsafe(surface); 77 } 78 /** \brief The function gets the next free surface from the free surfaces array. 79 * 80 * @note Surface is detached from the free surfaces array. 81 */ GetSurface()82 inline msdkFrameSurface* GetSurface() { 83 std::lock_guard<std::mutex> lock(m_rMutex); 84 return GetSurfaceUnsafe(); 85 } 86 87 private: AddSurfaceUnsafe(msdkFrameSurface * surface)88 inline void AddSurfaceUnsafe(msdkFrameSurface* surface) { 89 msdkFrameSurface* head; 90 91 MSDK_SELF_CHECK(surface); 92 MSDK_SELF_CHECK(!surface->prev); 93 MSDK_SELF_CHECK(!surface->next); 94 95 head = m_pSurfaces; 96 m_pSurfaces = surface; 97 m_pSurfaces->next = head; 98 } GetSurfaceUnsafe()99 inline msdkFrameSurface* GetSurfaceUnsafe() { 100 msdkFrameSurface* surface = NULL; 101 102 if (m_pSurfaces) { 103 surface = m_pSurfaces; 104 m_pSurfaces = m_pSurfaces->next; 105 surface->prev = surface->next = NULL; 106 MSDK_SELF_CHECK(!surface->prev); 107 MSDK_SELF_CHECK(!surface->next); 108 } 109 return surface; 110 } 111 112 protected: 113 msdkFrameSurface* m_pSurfaces; 114 std::mutex& m_rMutex; 115 116 private: 117 msdkFreeSurfacesPool(const msdkFreeSurfacesPool&); 118 void operator=(const msdkFreeSurfacesPool&); 119 }; 120 121 // random access, predicted as FIFO 122 class msdkUsedSurfacesPool { 123 friend class CBuffering; 124 125 public: msdkUsedSurfacesPool(std::mutex & mutex)126 msdkUsedSurfacesPool(std::mutex& mutex) 127 : m_pSurfacesHead(NULL), 128 m_pSurfacesTail(NULL), 129 m_rMutex(mutex) {} 130 ~msdkUsedSurfacesPool()131 ~msdkUsedSurfacesPool() { 132 m_pSurfacesHead = NULL; 133 m_pSurfacesTail = NULL; 134 } 135 136 /** \brief The function adds surface to the used surfaces array (m_pUsedSurfaces). 137 * 138 * @note That's caller responsibility to pass valid surface. 139 * @note We can't actually know which surface will be returned by the decoder or unlocked. However, 140 * we can make prediction that it will be the oldest surface. Thus, here the function adds new 141 * surface (youngest) to the tail of the least. Check operations for the list will run starting from 142 * head. 143 */ AddSurface(msdkFrameSurface * surface)144 inline void AddSurface(msdkFrameSurface* surface) { 145 std::lock_guard<std::mutex> lock(m_rMutex); 146 AddSurfaceUnsafe(surface); 147 } 148 149 /** \brief The function detaches surface from the used surfaces array. 150 * 151 * @note That's caller responsibility to pass valid surface. 152 */ 153 DetachSurface(msdkFrameSurface * surface)154 inline void DetachSurface(msdkFrameSurface* surface) { 155 std::lock_guard<std::mutex> lock(m_rMutex); 156 DetachSurfaceUnsafe(surface); 157 } 158 159 private: DetachSurfaceUnsafe(msdkFrameSurface * surface)160 inline void DetachSurfaceUnsafe(msdkFrameSurface* surface) { 161 MSDK_SELF_CHECK(surface); 162 163 msdkFrameSurface* prev = surface->prev; 164 msdkFrameSurface* next = surface->next; 165 166 if (prev) { 167 prev->next = next; 168 } 169 else { 170 MSDK_SELF_CHECK(surface == m_pSurfacesHead); 171 m_pSurfacesHead = next; 172 } 173 if (next) { 174 next->prev = prev; 175 } 176 else { 177 MSDK_SELF_CHECK(surface == m_pSurfacesTail); 178 m_pSurfacesTail = prev; 179 } 180 181 surface->prev = surface->next = NULL; 182 MSDK_SELF_CHECK(!surface->prev); 183 MSDK_SELF_CHECK(!surface->next); 184 } AddSurfaceUnsafe(msdkFrameSurface * surface)185 inline void AddSurfaceUnsafe(msdkFrameSurface* surface) { 186 MSDK_SELF_CHECK(surface); 187 MSDK_SELF_CHECK(!surface->prev); 188 MSDK_SELF_CHECK(!surface->next); 189 190 surface->prev = m_pSurfacesTail; 191 surface->next = NULL; 192 if (m_pSurfacesTail) { 193 m_pSurfacesTail->next = surface; 194 m_pSurfacesTail = m_pSurfacesTail->next; 195 } 196 else { 197 m_pSurfacesHead = m_pSurfacesTail = surface; 198 } 199 } 200 201 protected: 202 msdkFrameSurface* m_pSurfacesHead; // oldest surface 203 msdkFrameSurface* m_pSurfacesTail; // youngest surface 204 std::mutex& m_rMutex; 205 206 private: 207 msdkUsedSurfacesPool(const msdkUsedSurfacesPool&); 208 void operator=(const msdkUsedSurfacesPool&); 209 }; 210 211 // FIFO list of surfaces 212 class msdkOutputSurfacesPool { 213 friend class CBuffering; 214 215 public: msdkOutputSurfacesPool(std::mutex & mutex)216 msdkOutputSurfacesPool(std::mutex& mutex) 217 : m_pSurfacesHead(NULL), 218 m_pSurfacesTail(NULL), 219 m_SurfacesCount(0), 220 m_rMutex(mutex) {} 221 ~msdkOutputSurfacesPool()222 ~msdkOutputSurfacesPool() { 223 m_pSurfacesHead = NULL; 224 m_pSurfacesTail = NULL; 225 } 226 AddSurface(msdkOutputSurface * surface)227 inline void AddSurface(msdkOutputSurface* surface) { 228 std::lock_guard<std::mutex> lock(m_rMutex); 229 AddSurfaceUnsafe(surface); 230 } GetSurface()231 inline msdkOutputSurface* GetSurface() { 232 std::lock_guard<std::mutex> lock(m_rMutex); 233 return GetSurfaceUnsafe(); 234 } 235 GetSurfaceCount()236 inline mfxU32 GetSurfaceCount() { 237 return m_SurfacesCount; 238 } 239 240 private: AddSurfaceUnsafe(msdkOutputSurface * surface)241 inline void AddSurfaceUnsafe(msdkOutputSurface* surface) { 242 MSDK_SELF_CHECK(surface); 243 MSDK_SELF_CHECK(!surface->next); 244 surface->next = NULL; 245 246 if (m_pSurfacesTail) { 247 m_pSurfacesTail->next = surface; 248 m_pSurfacesTail = m_pSurfacesTail->next; 249 } 250 else { 251 m_pSurfacesHead = m_pSurfacesTail = surface; 252 } 253 ++m_SurfacesCount; 254 } GetSurfaceUnsafe()255 inline msdkOutputSurface* GetSurfaceUnsafe() { 256 msdkOutputSurface* surface = NULL; 257 258 if (m_pSurfacesHead) { 259 surface = m_pSurfacesHead; 260 m_pSurfacesHead = m_pSurfacesHead->next; 261 if (!m_pSurfacesHead) { 262 // there was only one surface in the array... 263 m_pSurfacesTail = NULL; 264 } 265 --m_SurfacesCount; 266 surface->next = NULL; 267 MSDK_SELF_CHECK(!surface->next); 268 } 269 return surface; 270 } 271 272 protected: 273 msdkOutputSurface* m_pSurfacesHead; // oldest surface 274 msdkOutputSurface* m_pSurfacesTail; // youngest surface 275 mfxU32 m_SurfacesCount; 276 std::mutex& m_rMutex; 277 278 private: 279 msdkOutputSurfacesPool(const msdkOutputSurfacesPool&); 280 void operator=(const msdkOutputSurfacesPool&); 281 }; 282 283 /** \brief Helper class defining optimal buffering operations for the Media SDK decoder. 284 */ 285 class CBuffering { 286 public: 287 CBuffering(); 288 virtual ~CBuffering(); 289 290 protected: // functions 291 mfxStatus AllocBuffers(mfxU32 SurfaceNumber); 292 mfxStatus AllocVppBuffers(mfxU32 VppSurfaceNumber); 293 void AllocOutputBuffer(); 294 void FreeBuffers(); 295 void ResetBuffers(); 296 void ResetVppBuffers(); 297 298 /** \brief The function syncs arrays of free and used surfaces. 299 * 300 * If Media SDK used surface for internal needs and unlocked it, the function moves such a surface 301 * back to the free surfaces array. 302 */ 303 void SyncFrameSurfaces(); 304 void SyncVppFrameSurfaces(); 305 306 /** \brief Returns surface which corresponds to the given one in Media SDK format (mfxFrameSurface1). 307 * 308 * @note This function will not detach the surface from the array, perform this explicitly. 309 */ FindUsedSurface(mfxFrameSurface1 * frame)310 inline msdkFrameSurface* FindUsedSurface(mfxFrameSurface1* frame) { 311 return (msdkFrameSurface*)(frame); 312 } 313 AddFreeOutputSurfaceUnsafe(msdkOutputSurface * surface)314 inline void AddFreeOutputSurfaceUnsafe(msdkOutputSurface* surface) { 315 msdkOutputSurface* head = m_pFreeOutputSurfaces; 316 317 MSDK_SELF_CHECK(surface); 318 MSDK_SELF_CHECK(!surface->next); 319 m_pFreeOutputSurfaces = surface; 320 m_pFreeOutputSurfaces->next = head; 321 } AddFreeOutputSurface(msdkOutputSurface * surface)322 inline void AddFreeOutputSurface(msdkOutputSurface* surface) { 323 std::lock_guard<std::mutex> lock(m_Mutex); 324 AddFreeOutputSurfaceUnsafe(surface); 325 } 326 GetFreeOutputSurfaceUnsafe(std::unique_lock<std::mutex> & lock)327 inline msdkOutputSurface* GetFreeOutputSurfaceUnsafe(std::unique_lock<std::mutex>& lock) { 328 msdkOutputSurface* surface = NULL; 329 330 if (!m_pFreeOutputSurfaces) { 331 lock.unlock(); 332 AllocOutputBuffer(); 333 lock.lock(); 334 } 335 if (m_pFreeOutputSurfaces) { 336 surface = m_pFreeOutputSurfaces; 337 m_pFreeOutputSurfaces = m_pFreeOutputSurfaces->next; 338 surface->next = NULL; 339 MSDK_SELF_CHECK(!surface->next); 340 } 341 return surface; 342 } GetFreeOutputSurface()343 inline msdkOutputSurface* GetFreeOutputSurface() { 344 std::unique_lock<std::mutex> lock(m_Mutex); 345 return GetFreeOutputSurfaceUnsafe(lock); 346 } 347 348 /** \brief Function returns surface data to the corresponding buffers. 349 */ ReturnSurfaceToBuffers(msdkOutputSurface * output_surface)350 inline void ReturnSurfaceToBuffers(msdkOutputSurface* output_surface) { 351 MSDK_SELF_CHECK(output_surface); 352 MSDK_SELF_CHECK(output_surface->surface); 353 MSDK_SELF_CHECK(output_surface->syncp); 354 355 msdk_atomic_dec16(&(output_surface->surface->render_lock)); 356 357 output_surface->surface = NULL; 358 output_surface->syncp = NULL; 359 360 AddFreeOutputSurface(output_surface); 361 } 362 363 protected: // variables 364 mfxU32 m_SurfacesNumber; 365 mfxU32 m_OutputSurfacesNumber; 366 msdkFrameSurface* m_pSurfaces; 367 msdkFrameSurface* m_pVppSurfaces; 368 std::mutex m_Mutex; 369 370 // LIFO list of frame surfaces 371 msdkFreeSurfacesPool m_FreeSurfacesPool; 372 msdkFreeSurfacesPool m_FreeVppSurfacesPool; 373 374 // random access, predicted as FIFO 375 msdkUsedSurfacesPool m_UsedSurfacesPool; 376 msdkUsedSurfacesPool m_UsedVppSurfacesPool; 377 378 // LIFO list of output surfaces 379 msdkOutputSurface* m_pFreeOutputSurfaces; 380 381 // FIFO list of surfaces 382 msdkOutputSurfacesPool m_OutputSurfacesPool; 383 msdkOutputSurfacesPool m_DeliveredSurfacesPool; 384 385 private: 386 CBuffering(const CBuffering&); 387 void operator=(const CBuffering&); 388 }; 389 390 #endif // __MFX_BUFFERING_H__ 391