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