Merge pull request #4905 from FernetMenta/skipframe
[vuplus_xbmc] / xbmc / cores / VideoRenderers / RenderManager.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "system.h"
22 #if defined(HAS_GL)
23   #include "system_gl.h"
24 #endif
25 #include "RenderManager.h"
26 #include "threads/CriticalSection.h"
27 #include "video/VideoReferenceClock.h"
28 #include "utils/MathUtils.h"
29 #include "threads/Atomics.h"
30 #include "threads/SingleLock.h"
31 #include "utils/log.h"
32 #include "utils/TimeUtils.h"
33 #include "utils/StringUtils.h"
34
35 #include "Application.h"
36 #include "ApplicationMessenger.h"
37 #include "settings/AdvancedSettings.h"
38 #include "settings/MediaSettings.h"
39 #include "settings/Settings.h"
40 #include "guilib/GUIFontManager.h"
41
42 #if defined(HAS_GL)
43   #include "LinuxRendererGL.h"
44 #elif HAS_GLES == 2
45   #include "LinuxRendererGLES.h"
46 #elif defined(HAS_DX)
47   #include "WinRenderer.h"
48 #elif defined(HAS_SDL)
49   #include "LinuxRenderer.h"
50 #endif
51
52 #include "RenderCapture.h"
53
54 /* to use the same as player */
55 #include "../dvdplayer/DVDClock.h"
56 #include "../dvdplayer/DVDCodecs/Video/DVDVideoCodec.h"
57 #include "../dvdplayer/DVDCodecs/DVDCodecUtils.h"
58
59 #define MAXPRESENTDELAY 0.500
60
61 /* at any point we want an exclusive lock on rendermanager */
62 /* we must make sure we don't have a graphiccontext lock */
63 /* these two functions allow us to step out from that lock */
64 /* and reaquire it after having the exclusive lock */
65
66 template<class T>
67 class CRetakeLock
68 {
69 public:
70   CRetakeLock(CSharedSection &section, CCriticalSection &owned = g_graphicsContext)
71     : m_count(owned.exit())
72     , m_lock (section),
73       m_owned(owned)
74   {
75     m_owned.restore(m_count);
76   }
77
78   void Leave() { m_lock.Leave(); }
79   void Enter()
80   {
81     m_count = m_owned.exit();
82     m_lock.Enter();
83     m_owned.restore(m_count);
84   }
85
86 private:
87   DWORD             m_count;
88   T                 m_lock;
89   CCriticalSection &m_owned;
90 };
91
92 static void requeue(std::deque<int> &trg, std::deque<int> &src)
93 {
94   trg.push_back(src.front());
95   src.pop_front();
96 }
97
98 CXBMCRenderManager::CXBMCRenderManager()
99 {
100   m_pRenderer = NULL;
101   m_bIsStarted = false;
102
103   m_presentstep = PRESENT_IDLE;
104   m_rendermethod = 0;
105   m_presentsource = 0;
106   m_bReconfigured = false;
107   m_hasCaptures = false;
108   m_displayLatency = 0.0f;
109   m_presentcorr = 0.0;
110   m_presenterr = 0.0;
111   memset(&m_errorbuff, 0, ERRORBUFFSIZE);
112   m_errorindex = 0;
113   m_QueueSize   = 2;
114   m_QueueSkip   = 0;
115   m_format      = RENDER_FMT_NONE;
116 }
117
118 CXBMCRenderManager::~CXBMCRenderManager()
119 {
120   delete m_pRenderer;
121   m_pRenderer = NULL;
122 }
123
124 void CXBMCRenderManager::GetVideoRect(CRect &source, CRect &dest)
125 {
126   CSharedLock lock(m_sharedSection);
127   if (m_pRenderer)
128     m_pRenderer->GetVideoRect(source, dest);
129 }
130
131 float CXBMCRenderManager::GetAspectRatio()
132 {
133   CSharedLock lock(m_sharedSection);
134   if (m_pRenderer)
135     return m_pRenderer->GetAspectRatio();
136   else
137     return 1.0f;
138 }
139
140 /* These is based on CurrentHostCounter() */
141 double CXBMCRenderManager::GetPresentTime()
142 {
143   return CDVDClock::GetAbsoluteClock(false) / DVD_TIME_BASE;
144 }
145
146 static double wrap(double x, double minimum, double maximum)
147 {
148   if(x >= minimum
149   && x <= maximum)
150     return x;
151   x = fmod(x - minimum, maximum - minimum) + minimum;
152   if(x < minimum)
153     x += maximum - minimum;
154   if(x > maximum)
155     x -= maximum - minimum;
156   return x;
157 }
158
159 void CXBMCRenderManager::WaitPresentTime(double presenttime)
160 {
161   double frametime;
162   int fps = g_VideoReferenceClock.GetRefreshRate(&frametime);
163   if(fps <= 0)
164   {
165     /* smooth video not enabled */
166     CDVDClock::WaitAbsoluteClock(presenttime * DVD_TIME_BASE);
167     return;
168   }
169
170   double clock     = CDVDClock::WaitAbsoluteClock(presenttime * DVD_TIME_BASE) / DVD_TIME_BASE;
171   double target    = 0.5;
172   double error     = ( clock - presenttime ) / frametime - target;
173
174   m_presenterr     = error;
175
176   // correct error so it targets the closest vblank
177   error = wrap(error, 0.0 - target, 1.0 - target);
178
179   // scale the error used for correction,
180   // based on how much buffer we have on
181   // that side of the target
182   if(error > 0)
183     error /= 2.0 * (1.0 - target);
184   if(error < 0)
185     error /= 2.0 * (0.0 + target);
186
187   //save error in the buffer
188   m_errorindex = (m_errorindex + 1) % ERRORBUFFSIZE;
189   m_errorbuff[m_errorindex] = error;
190
191   //get the average error from the buffer
192   double avgerror = 0.0;
193   for (int i = 0; i < ERRORBUFFSIZE; i++)
194     avgerror += m_errorbuff[i];
195
196   avgerror /= ERRORBUFFSIZE;
197
198
199   //we change the clock speed slightly
200   //to make every frame's presenttime end up in the middle of two vblanks
201   //integral correction, clamp to -0.5:0.5 range
202   m_presentcorr = std::max(std::min(m_presentcorr + avgerror * 0.01, 0.1), -0.1);
203   g_VideoReferenceClock.SetFineAdjust(1.0 - avgerror * 0.01 - m_presentcorr * 0.01);
204
205   //printf("%f %f % 2.0f%% % f % f\n", presenttime, clock, m_presentcorr * 100, error, error_org);
206 }
207
208 CStdString CXBMCRenderManager::GetVSyncState()
209 {
210   double avgerror = 0.0;
211   for (int i = 0; i < ERRORBUFFSIZE; i++)
212     avgerror += m_errorbuff[i];
213   avgerror /= ERRORBUFFSIZE;
214
215   CStdString state = StringUtils::Format("sync:%+3d%% avg:%3d%% error:%2d%%"
216                                          ,     MathUtils::round_int(m_presentcorr * 100)
217                                          ,     MathUtils::round_int(avgerror      * 100)
218                                          , abs(MathUtils::round_int(m_presenterr  * 100)));
219   return state;
220 }
221
222 bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsigned int d_width, unsigned int d_height, float fps, unsigned flags, ERenderFormat format, unsigned extended_format, unsigned int orientation, int buffers)
223 {
224
225   CSingleLock    lock2(m_presentlock);
226
227   /* make sure any queued frame was fully presented */
228   XbmcThreads::EndTime endtime(5000);
229   while(m_presentstep != PRESENT_IDLE)
230   {
231     if(endtime.IsTimePast())
232     {
233       CLog::Log(LOGWARNING, "CRenderManager::Configure - timeout waiting for state");
234       return false;
235     }
236     m_presentevent.wait(lock2, endtime.MillisLeft());
237   };
238   lock2.Leave();
239
240   CExclusiveLock lock(m_sharedSection);
241   if(!m_pRenderer)
242   {
243     CLog::Log(LOGERROR, "%s called without a valid Renderer object", __FUNCTION__);
244     return false;
245   }
246
247
248   bool result = m_pRenderer->Configure(width, height, d_width, d_height, fps, flags, format, extended_format, orientation);
249   if(result)
250   {
251     if( flags & CONF_FLAGS_FULLSCREEN )
252     {
253       lock.Leave();
254       CApplicationMessenger::Get().SwitchToFullscreen();
255       lock.Enter();
256     }
257     lock2.Enter();
258     m_format = format;
259
260     int processor = m_pRenderer->GetProcessorSize();
261     if(processor > buffers)                          /* DXVA-HD returns processor size 6 */
262       m_QueueSize = 3;                               /* we need queue size of 3 to get future frames in the processor */
263     else if(processor)
264       m_QueueSize = buffers - processor + 1;         /* respect maximum refs */
265     else
266       m_QueueSize = m_pRenderer->GetMaxBufferSize(); /* no refs to data */
267
268     m_QueueSize = std::min(m_QueueSize, (int)m_pRenderer->GetMaxBufferSize());
269     m_QueueSize = std::min(m_QueueSize, NUM_BUFFERS);
270     if(m_QueueSize < 2)
271     {
272       m_QueueSize = 2;
273       CLog::Log(LOGWARNING, "CXBMCRenderManager::Configure - queue size too small (%d, %d, %d)", m_QueueSize, processor, buffers);
274     }
275
276     m_pRenderer->SetBufferSize(m_QueueSize);
277     m_pRenderer->Update();
278
279     m_queued.clear();
280     m_discard.clear();
281     m_free.clear();
282     m_presentsource = 0;
283     for (int i=1; i < m_QueueSize; i++)
284       m_free.push_back(i);
285
286     m_bIsStarted = true;
287     m_bReconfigured = true;
288     m_presentstep = PRESENT_IDLE;
289     m_presentevent.notifyAll();
290
291     m_firstFlipPage = false;  // tempfix
292
293     CLog::Log(LOGDEBUG, "CXBMCRenderManager::Configure - %d", m_QueueSize);
294   }
295
296   return result;
297 }
298
299 bool CXBMCRenderManager::RendererHandlesPresent() const
300 {
301   return IsConfigured() && (m_firstFlipPage || m_format != RENDER_FMT_BYPASS);
302 }
303
304 bool CXBMCRenderManager::IsConfigured() const
305 {
306   if (!m_pRenderer)
307     return false;
308   return m_pRenderer->IsConfigured();
309 }
310
311 void CXBMCRenderManager::Update()
312 {
313   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
314
315   if (m_pRenderer)
316     m_pRenderer->Update();
317 }
318
319 bool CXBMCRenderManager::FrameWait(int ms)
320 {
321   XbmcThreads::EndTime timeout(ms);
322   CSingleLock lock(m_presentlock);
323   while(m_presentstep == PRESENT_IDLE && !timeout.IsTimePast())
324     m_presentevent.wait(lock, timeout.MillisLeft());
325   return m_presentstep != PRESENT_IDLE;
326 }
327
328 void CXBMCRenderManager::FrameMove()
329 {
330   { CSharedLock lock(m_sharedSection);
331     CSingleLock lock2(m_presentlock);
332
333     if (!m_pRenderer)
334       return;
335
336     if (m_presentstep == PRESENT_FRAME2)
337     {
338       if(!m_queued.empty())
339       {
340         double timestamp = GetPresentTime();
341         SPresent& m = m_Queue[m_presentsource];
342         SPresent& q = m_Queue[m_queued.front()];
343         if(timestamp > m.timestamp + (q.timestamp - m.timestamp) * 0.5)
344         {
345           m_presentstep = PRESENT_READY;
346           m_presentevent.notifyAll();
347         }
348       }
349     }
350
351     if (m_presentstep == PRESENT_READY)
352       PrepareNextRender();
353
354     if(m_presentstep == PRESENT_FLIP)
355     {
356       m_pRenderer->FlipPage(m_presentsource);
357       m_presentstep = PRESENT_FRAME;
358       m_presentevent.notifyAll();
359     }
360
361     /* release all previous */
362     for(std::deque<int>::iterator it = m_discard.begin(); it != m_discard.end(); )
363     {
364       // TODO check for fence
365       m_pRenderer->ReleaseBuffer(*it);
366       m_overlays.Release(*it);
367       m_free.push_back(*it);
368       it = m_discard.erase(it);
369     }
370   }
371 }
372
373 void CXBMCRenderManager::FrameFinish()
374 {
375   /* wait for this present to be valid */
376   SPresent& m = m_Queue[m_presentsource];
377
378   if(g_graphicsContext.IsFullScreenVideo())
379     WaitPresentTime(m.timestamp);
380
381   m_clock_framefinish = GetPresentTime();
382
383   { CSingleLock lock(m_presentlock);
384
385     if(m_presentstep == PRESENT_FRAME)
386     {
387       if( m.presentmethod == PRESENT_METHOD_BOB
388       ||  m.presentmethod == PRESENT_METHOD_WEAVE)
389         m_presentstep = PRESENT_FRAME2;
390       else
391         m_presentstep = PRESENT_IDLE;
392     }
393     else if(m_presentstep == PRESENT_FRAME2)
394       m_presentstep = PRESENT_IDLE;
395
396
397     if(m_presentstep == PRESENT_IDLE)
398     {
399       if(!m_queued.empty())
400         m_presentstep = PRESENT_READY;
401     }
402
403     m_presentevent.notifyAll();
404   }
405 }
406
407 unsigned int CXBMCRenderManager::PreInit()
408 {
409   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
410
411   m_presentcorr = 0.0;
412   m_presenterr  = 0.0;
413   m_errorindex  = 0;
414   memset(m_errorbuff, 0, sizeof(m_errorbuff));
415
416   m_bIsStarted = false;
417   if (!m_pRenderer)
418   {
419 #if defined(HAS_GL)
420     m_pRenderer = new CLinuxRendererGL();
421 #elif HAS_GLES == 2
422     m_pRenderer = new CLinuxRendererGLES();
423 #elif defined(HAS_DX)
424     m_pRenderer = new CWinRenderer();
425 #elif defined(HAS_SDL)
426     m_pRenderer = new CLinuxRenderer();
427 #endif
428   }
429
430   UpdateDisplayLatency();
431
432   m_QueueSize   = 2;
433   m_QueueSkip   = 0;
434
435   return m_pRenderer->PreInit();
436 }
437
438 void CXBMCRenderManager::UnInit()
439 {
440   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
441
442   m_bIsStarted = false;
443
444   m_overlays.Flush();
445   g_fontManager.Unload("__subtitle__");
446   g_fontManager.Unload("__subtitleborder__");
447
448   // free renderer resources.
449   // TODO: we may also want to release the renderer here.
450   if (m_pRenderer)
451     m_pRenderer->UnInit();
452 }
453
454 bool CXBMCRenderManager::Flush()
455 {
456   if (!m_pRenderer)
457     return true;
458
459   if (g_application.IsCurrentThread())
460   {
461     CLog::Log(LOGDEBUG, "%s - flushing renderer", __FUNCTION__);
462
463     CRetakeLock<CExclusiveLock> lock(m_sharedSection);
464     m_pRenderer->Flush();
465     m_overlays.Flush();
466     m_flushEvent.Set();
467   }
468   else
469   {
470     ThreadMessage msg = {TMSG_RENDERER_FLUSH};
471     m_flushEvent.Reset();
472     CApplicationMessenger::Get().SendMessage(msg, false);
473     if (!m_flushEvent.WaitMSec(1000))
474     {
475       CLog::Log(LOGERROR, "%s - timed out waiting for renderer to flush", __FUNCTION__);
476       return false;
477     }
478     else
479       return true;
480   }
481   return true;
482 }
483
484 void CXBMCRenderManager::SetupScreenshot()
485 {
486   CSharedLock lock(m_sharedSection);
487   if (m_pRenderer)
488     m_pRenderer->SetupScreenshot();
489 }
490
491 CRenderCapture* CXBMCRenderManager::AllocRenderCapture()
492 {
493   return new CRenderCapture;
494 }
495
496 void CXBMCRenderManager::ReleaseRenderCapture(CRenderCapture* capture)
497 {
498   CSingleLock lock(m_captCritSect);
499
500   RemoveCapture(capture);
501
502   //because a CRenderCapture might have some gl things allocated, it can only be deleted from app thread
503   if (g_application.IsCurrentThread())
504   {
505     delete capture;
506   }
507   else
508   {
509     capture->SetState(CAPTURESTATE_NEEDSDELETE);
510     m_captures.push_back(capture);
511   }
512
513   if (!m_captures.empty())
514     m_hasCaptures = true;
515 }
516
517 void CXBMCRenderManager::Capture(CRenderCapture* capture, unsigned int width, unsigned int height, int flags)
518 {
519   CSingleLock lock(m_captCritSect);
520
521   RemoveCapture(capture);
522
523   capture->SetState(CAPTURESTATE_NEEDSRENDER);
524   capture->SetUserState(CAPTURESTATE_WORKING);
525   capture->SetWidth(width);
526   capture->SetHeight(height);
527   capture->SetFlags(flags);
528   capture->GetEvent().Reset();
529
530   if (g_application.IsCurrentThread())
531   {
532     if (flags & CAPTUREFLAG_IMMEDIATELY)
533     {
534       //render capture and read out immediately
535       RenderCapture(capture);
536       capture->SetUserState(capture->GetState());
537       capture->GetEvent().Set();
538     }
539
540     if ((flags & CAPTUREFLAG_CONTINUOUS) || !(flags & CAPTUREFLAG_IMMEDIATELY))
541     {
542       //schedule this capture for a render and readout
543       m_captures.push_back(capture);
544     }
545   }
546   else
547   {
548     //schedule this capture for a render and readout
549     m_captures.push_back(capture);
550   }
551
552   if (!m_captures.empty())
553     m_hasCaptures = true;
554 }
555
556 void CXBMCRenderManager::ManageCaptures()
557 {
558   //no captures, return here so we don't do an unnecessary lock
559   if (!m_hasCaptures)
560     return;
561
562   CSingleLock lock(m_captCritSect);
563
564   std::list<CRenderCapture*>::iterator it = m_captures.begin();
565   while (it != m_captures.end())
566   {
567     CRenderCapture* capture = *it;
568
569     if (capture->GetState() == CAPTURESTATE_NEEDSDELETE)
570     {
571       delete capture;
572       it = m_captures.erase(it);
573       continue;
574     }
575
576     if (capture->GetState() == CAPTURESTATE_NEEDSRENDER)
577       RenderCapture(capture);
578     else if (capture->GetState() == CAPTURESTATE_NEEDSREADOUT)
579       capture->ReadOut();
580
581     if (capture->GetState() == CAPTURESTATE_DONE || capture->GetState() == CAPTURESTATE_FAILED)
582     {
583       //tell the thread that the capture is done or has failed
584       capture->SetUserState(capture->GetState());
585       capture->GetEvent().Set();
586
587       if (capture->GetFlags() & CAPTUREFLAG_CONTINUOUS)
588       {
589         capture->SetState(CAPTURESTATE_NEEDSRENDER);
590
591         //if rendering this capture continuously, and readout is async, render a new capture immediately
592         if (capture->IsAsync() && !(capture->GetFlags() & CAPTUREFLAG_IMMEDIATELY))
593           RenderCapture(capture);
594
595         ++it;
596       }
597       else
598       {
599         it = m_captures.erase(it);
600       }
601     }
602     else
603     {
604       ++it;
605     }
606   }
607
608   if (m_captures.empty())
609     m_hasCaptures = false;
610 }
611
612 void CXBMCRenderManager::RenderCapture(CRenderCapture* capture)
613 {
614   CSharedLock lock(m_sharedSection);
615   if (!m_pRenderer || !m_pRenderer->RenderCapture(capture))
616     capture->SetState(CAPTURESTATE_FAILED);
617 }
618
619 void CXBMCRenderManager::RemoveCapture(CRenderCapture* capture)
620 {
621   //remove this CRenderCapture from the list
622   std::list<CRenderCapture*>::iterator it;
623   while ((it = find(m_captures.begin(), m_captures.end(), capture)) != m_captures.end())
624     m_captures.erase(it);
625 }
626
627 void CXBMCRenderManager::SetViewMode(int iViewMode)
628 {
629   CSharedLock lock(m_sharedSection);
630   if (m_pRenderer)
631     m_pRenderer->SetViewMode(iViewMode);
632 }
633
634 void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
635 {
636   { CSharedLock lock(m_sharedSection);
637
638     if(bStop)
639       return;
640
641     if(!m_pRenderer) return;
642
643     m_firstFlipPage = true;              // tempfix
644
645     EPRESENTMETHOD presentmethod;
646
647     EDEINTERLACEMODE deinterlacemode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
648     EINTERLACEMETHOD interlacemethod = AutoInterlaceMethodInternal(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
649
650     if(g_advancedSettings.m_videoDisableBackgroundDeinterlace && !g_graphicsContext.IsFullScreenVideo())
651       deinterlacemode = VS_DEINTERLACEMODE_OFF;
652
653     if (deinterlacemode == VS_DEINTERLACEMODE_OFF)
654       presentmethod = PRESENT_METHOD_SINGLE;
655     else
656     {
657       if (deinterlacemode == VS_DEINTERLACEMODE_AUTO && sync == FS_NONE)
658         presentmethod = PRESENT_METHOD_SINGLE;
659       else
660       {
661         bool invert = false;
662         if      (interlacemethod == VS_INTERLACEMETHOD_RENDER_BLEND)            presentmethod = PRESENT_METHOD_BLEND;
663         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE)            presentmethod = PRESENT_METHOD_WEAVE;
664         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED) { presentmethod = PRESENT_METHOD_WEAVE ; invert = true; }
665         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB)              presentmethod = PRESENT_METHOD_BOB;
666         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED)   { presentmethod = PRESENT_METHOD_BOB; invert = true; }
667         else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BOB)                presentmethod = PRESENT_METHOD_BOB;
668         else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BEST)               presentmethod = PRESENT_METHOD_BOB;
669         else                                                                    presentmethod = PRESENT_METHOD_SINGLE;
670
671         /* default to odd field if we want to deinterlace and don't know better */
672         if (deinterlacemode == VS_DEINTERLACEMODE_FORCE && sync == FS_NONE)
673           sync = FS_TOP;
674
675         /* invert present field */
676         if(invert)
677         {
678           if( sync == FS_BOT )
679             sync = FS_TOP;
680           else
681             sync = FS_BOT;
682         }
683       }
684     }
685
686     /* failsafe for invalid timestamps, to make sure queue always empties */
687     if(timestamp > GetPresentTime() + 5.0)
688       timestamp = GetPresentTime() + 5.0;
689
690     CSingleLock lock2(m_presentlock);
691
692     if(m_free.empty())
693       return;
694
695     if(source < 0)
696       source = m_free.front();
697
698     SPresent& m = m_Queue[source];
699     m.timestamp     = timestamp;
700     m.presentfield  = sync;
701     m.presentmethod = presentmethod;
702     requeue(m_queued, m_free);
703
704     /* signal to any waiters to check state */
705     if(m_presentstep == PRESENT_IDLE)
706     {
707       m_presentstep = PRESENT_READY;
708       m_presentevent.notifyAll();
709     }
710   }
711 }
712
713 void CXBMCRenderManager::Reset()
714 {
715   CSharedLock lock(m_sharedSection);
716   if (m_pRenderer)
717     m_pRenderer->Reset();
718 }
719
720 RESOLUTION CXBMCRenderManager::GetResolution()
721 {
722   CSharedLock lock(m_sharedSection);
723   if (m_pRenderer)
724     return m_pRenderer->GetResolution();
725   else
726     return RES_INVALID;
727 }
728
729 float CXBMCRenderManager::GetMaximumFPS()
730 {
731   float fps;
732
733   if (CSettings::Get().GetInt("videoscreen.vsync") != VSYNC_DISABLED)
734   {
735     fps = (float)g_VideoReferenceClock.GetRefreshRate();
736     if (fps <= 0) fps = g_graphicsContext.GetFPS();
737   }
738   else
739     fps = 1000.0f;
740
741   return fps;
742 }
743
744 void CXBMCRenderManager::RegisterRenderUpdateCallBack(const void *ctx, RenderUpdateCallBackFn fn)
745 {
746   if (m_pRenderer)
747     m_pRenderer->RegisterRenderUpdateCallBack(ctx, fn);
748 }
749
750 void CXBMCRenderManager::RegisterRenderFeaturesCallBack(const void *ctx, RenderFeaturesCallBackFn fn)
751 {
752   if (m_pRenderer)
753     m_pRenderer->RegisterRenderFeaturesCallBack(ctx, fn);
754 }
755
756 void CXBMCRenderManager::Render(bool clear, DWORD flags, DWORD alpha)
757 {
758   CSharedLock lock(m_sharedSection);
759
760   SPresent& m = m_Queue[m_presentsource];
761
762   if( m.presentmethod == PRESENT_METHOD_BOB )
763     PresentFields(clear, flags, alpha);
764   else if( m.presentmethod == PRESENT_METHOD_WEAVE )
765     PresentFields(clear, flags | RENDER_FLAG_WEAVE, alpha);
766   else if( m.presentmethod == PRESENT_METHOD_BLEND )
767     PresentBlend(clear, flags, alpha);
768   else
769     PresentSingle(clear, flags, alpha);
770
771   m_overlays.Render(m_presentsource);
772 }
773
774 /* simple present method */
775 void CXBMCRenderManager::PresentSingle(bool clear, DWORD flags, DWORD alpha)
776 {
777   CSingleLock lock(g_graphicsContext);
778
779   m_pRenderer->RenderUpdate(clear, flags, alpha);
780 }
781
782 /* new simpler method of handling interlaced material, *
783  * we just render the two fields right after eachother */
784 void CXBMCRenderManager::PresentFields(bool clear, DWORD flags, DWORD alpha)
785 {
786   CSingleLock lock(g_graphicsContext);
787   SPresent& m = m_Queue[m_presentsource];
788
789   if(m_presentstep == PRESENT_FRAME)
790   {
791     if( m.presentfield == FS_BOT)
792       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD0, alpha);
793     else
794       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD0, alpha);
795   }
796   else
797   {
798     if( m.presentfield == FS_TOP)
799       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD1, alpha);
800     else
801       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD1, alpha);
802   }
803 }
804
805 void CXBMCRenderManager::PresentBlend(bool clear, DWORD flags, DWORD alpha)
806 {
807   CSingleLock lock(g_graphicsContext);
808   SPresent& m = m_Queue[m_presentsource];
809
810   if( m.presentfield == FS_BOT )
811   {
812     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_NOOSD, alpha);
813     m_pRenderer->RenderUpdate(false, flags | RENDER_FLAG_TOP, alpha / 2);
814   }
815   else
816   {
817     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_NOOSD, alpha);
818     m_pRenderer->RenderUpdate(false, flags | RENDER_FLAG_BOT, alpha / 2);
819   }
820 }
821
822 void CXBMCRenderManager::Recover()
823 {
824 #if defined(HAS_GL) && !defined(TARGET_DARWIN)
825   glFlush(); // attempt to have gpu done with pixmap and vdpau
826 #endif
827
828   UpdateDisplayLatency();
829 }
830
831 void CXBMCRenderManager::UpdateDisplayLatency()
832 {
833   float refresh = g_graphicsContext.GetFPS();
834   if (g_graphicsContext.GetVideoResolution() == RES_WINDOW)
835     refresh = 0; // No idea about refresh rate when windowed, just get the default latency
836   m_displayLatency = (double) g_advancedSettings.GetDisplayLatency(refresh);
837   CLog::Log(LOGDEBUG, "CRenderManager::UpdateDisplayLatency - Latency set to %1.0f msec", m_displayLatency * 1000.0f);
838 }
839
840 void CXBMCRenderManager::UpdateResolution()
841 {
842   if (m_bReconfigured)
843   {
844     CRetakeLock<CExclusiveLock> lock(m_sharedSection);
845     if (g_graphicsContext.IsFullScreenVideo() && g_graphicsContext.IsFullScreenRoot())
846     {
847       RESOLUTION res = GetResolution();
848       g_graphicsContext.SetVideoResolution(res);
849     }
850     m_bReconfigured = false;
851   }
852 }
853
854
855 unsigned int CXBMCRenderManager::GetProcessorSize()
856 {
857   CSharedLock lock(m_sharedSection);
858   return std::max(4, NUM_BUFFERS);
859 }
860
861 // Supported pixel formats, can be called before configure
862 std::vector<ERenderFormat> CXBMCRenderManager::SupportedFormats()
863 {
864   CSharedLock lock(m_sharedSection);
865   if (m_pRenderer)
866     return m_pRenderer->SupportedFormats();
867   return std::vector<ERenderFormat>();
868 }
869
870 int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
871 {
872   CSharedLock lock(m_sharedSection);
873   if (!m_pRenderer)
874     return -1;
875
876   int index;
877   {
878     CSingleLock lock(m_presentlock);
879     if (m_free.empty())
880       return -1;
881     index = m_free.front();
882   }
883
884   if(m_pRenderer->AddVideoPicture(&pic, index))
885     return 1;
886
887   YV12Image image;
888   if (m_pRenderer->GetImage(&image, index) < 0)
889     return -1;
890
891   if(pic.format == RENDER_FMT_YUV420P
892   || pic.format == RENDER_FMT_YUV420P10
893   || pic.format == RENDER_FMT_YUV420P16)
894   {
895     CDVDCodecUtils::CopyPicture(&image, &pic);
896   }
897   else if(pic.format == RENDER_FMT_NV12)
898   {
899     CDVDCodecUtils::CopyNV12Picture(&image, &pic);
900   }
901   else if(pic.format == RENDER_FMT_YUYV422
902        || pic.format == RENDER_FMT_UYVY422)
903   {
904     CDVDCodecUtils::CopyYUV422PackedPicture(&image, &pic);
905   }
906   else if(pic.format == RENDER_FMT_DXVA)
907   {
908     CDVDCodecUtils::CopyDXVA2Picture(&image, &pic);
909   }
910 #ifdef HAVE_LIBVDPAU
911   else if(pic.format == RENDER_FMT_VDPAU
912        || pic.format == RENDER_FMT_VDPAU_420)
913     m_pRenderer->AddProcessor(pic.vdpau, index);
914 #endif
915 #ifdef HAVE_LIBOPENMAX
916   else if(pic.format == RENDER_FMT_OMXEGL)
917     m_pRenderer->AddProcessor(pic.openMax, &pic, index);
918 #endif
919 #ifdef TARGET_DARWIN
920   else if(pic.format == RENDER_FMT_CVBREF)
921     m_pRenderer->AddProcessor(pic.cvBufferRef, index);
922 #endif
923 #ifdef HAVE_LIBVA
924   else if(pic.format == RENDER_FMT_VAAPI)
925     m_pRenderer->AddProcessor(*pic.vaapi, index);
926 #endif
927 #ifdef HAS_LIBSTAGEFRIGHT
928   else if(pic.format == RENDER_FMT_EGLIMG)
929     m_pRenderer->AddProcessor(pic.stf, pic.eglimg, index);
930 #endif
931 #if defined(TARGET_ANDROID)
932   else if(pic.format == RENDER_FMT_MEDIACODEC)
933     m_pRenderer->AddProcessor(pic.mediacodec, index);
934 #endif
935
936   m_pRenderer->ReleaseImage(index, false);
937
938   return index;
939 }
940
941 bool CXBMCRenderManager::Supports(ERENDERFEATURE feature)
942 {
943   CSharedLock lock(m_sharedSection);
944   if (m_pRenderer)
945     return m_pRenderer->Supports(feature);
946   else
947     return false;
948 }
949
950 bool CXBMCRenderManager::Supports(EDEINTERLACEMODE method)
951 {
952   CSharedLock lock(m_sharedSection);
953   if (m_pRenderer)
954     return m_pRenderer->Supports(method);
955   else
956     return false;
957 }
958
959 bool CXBMCRenderManager::Supports(EINTERLACEMETHOD method)
960 {
961   CSharedLock lock(m_sharedSection);
962   if (m_pRenderer)
963     return m_pRenderer->Supports(method);
964   else
965     return false;
966 }
967
968 bool CXBMCRenderManager::Supports(ESCALINGMETHOD method)
969 {
970   CSharedLock lock(m_sharedSection);
971   if (m_pRenderer)
972     return m_pRenderer->Supports(method);
973   else
974     return false;
975 }
976
977 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethod(EINTERLACEMETHOD mInt)
978 {
979   CSharedLock lock(m_sharedSection);
980   return AutoInterlaceMethodInternal(mInt);
981 }
982
983 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethodInternal(EINTERLACEMETHOD mInt)
984 {
985   if (mInt == VS_INTERLACEMETHOD_NONE)
986     return VS_INTERLACEMETHOD_NONE;
987
988   if(!m_pRenderer->Supports(mInt))
989     mInt = VS_INTERLACEMETHOD_AUTO;
990
991   if (mInt == VS_INTERLACEMETHOD_AUTO)
992     return m_pRenderer->AutoInterlaceMethod();
993
994   return mInt;
995 }
996
997 int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop, int timeout)
998 {
999   CSingleLock lock2(m_presentlock);
1000
1001   XbmcThreads::EndTime endtime(timeout);
1002   while(m_free.empty())
1003   {
1004     m_presentevent.wait(lock2, std::min(50, timeout));
1005     if(endtime.IsTimePast() || bStop)
1006     {
1007       if (timeout != 0 && !bStop)
1008         CLog::Log(LOGWARNING, "CRenderManager::WaitForBuffer - timeout waiting for buffer");
1009       return -1;
1010     }
1011   }
1012
1013   // make sure overlay buffer is released, this won't happen on AddOverlay
1014   m_overlays.Release(m_free.front());
1015
1016   // return buffer level
1017   return m_queued.size() + m_discard.size();;
1018 }
1019
1020 void CXBMCRenderManager::PrepareNextRender()
1021 {
1022   CSingleLock lock(m_presentlock);
1023
1024   if (m_queued.empty())
1025   {
1026     CLog::Log(LOGERROR, "CRenderManager::PrepareNextRender - asked to prepare with nothing available");
1027     m_presentstep = PRESENT_IDLE;
1028     m_presentevent.notifyAll();
1029     return;
1030   }
1031
1032   double clocktime = GetPresentTime();
1033   double frametime = 1.0 / GetMaximumFPS();
1034   double correction = 0.0;
1035   int fps = g_VideoReferenceClock.GetRefreshRate();
1036   if((fps > 0) && g_graphicsContext.IsFullScreenVideo() && (clocktime != m_clock_framefinish))
1037   {
1038     correction = frametime;
1039   }
1040
1041   /* see if any future queued frames are already due */
1042   std::deque<int>::reverse_iterator curr, prev;
1043   int idx;
1044   curr = prev = m_queued.rbegin();
1045   ++prev;
1046   while (prev != m_queued.rend())
1047   {
1048     if(clocktime > m_Queue[*prev].timestamp + correction                 /* previous frame is late */
1049     && clocktime > m_Queue[*curr].timestamp - frametime + correction)    /* selected frame is close to it's display time */
1050       break;
1051     ++curr;
1052     ++prev;
1053   }
1054   idx = *curr;
1055
1056   /* in fullscreen we will block after render, but only for MAXPRESENTDELAY */
1057   bool next;
1058   if(g_graphicsContext.IsFullScreenVideo())
1059     next = (m_Queue[idx].timestamp <= clocktime + MAXPRESENTDELAY);
1060   else
1061     next = (m_Queue[idx].timestamp <= clocktime + frametime);
1062
1063   if (next)
1064   {
1065     /* skip late frames */
1066     while(m_queued.front() != idx)
1067     {
1068       requeue(m_discard, m_queued);
1069       m_QueueSkip++;
1070     }
1071
1072     m_presentstep   = PRESENT_FLIP;
1073     m_discard.push_back(m_presentsource);
1074     m_presentsource = idx;
1075     m_queued.pop_front();
1076     m_presentevent.notifyAll();
1077   }
1078 }
1079
1080 void CXBMCRenderManager::DiscardBuffer()
1081 {
1082   CSharedLock lock(m_sharedSection);
1083   CSingleLock lock2(m_presentlock);
1084
1085   while(!m_queued.empty())
1086     requeue(m_discard, m_queued);
1087
1088   if(m_presentstep == PRESENT_READY)
1089     m_presentstep   = PRESENT_IDLE;
1090   m_presentevent.notifyAll();
1091 }