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