Merge pull request #2749 from kylhill/master
[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     m_pRenderer->AddProcessor(pic.vdpau, index);
922 #endif
923 #ifdef HAVE_LIBOPENMAX
924   else if(pic.format == RENDER_FMT_OMXEGL)
925     m_pRenderer->AddProcessor(pic.openMax, &pic, index);
926 #endif
927 #ifdef TARGET_DARWIN
928   else if(pic.format == RENDER_FMT_CVBREF)
929     m_pRenderer->AddProcessor(pic.cvBufferRef, index);
930 #endif
931 #ifdef HAVE_LIBVA
932   else if(pic.format == RENDER_FMT_VAAPI)
933     m_pRenderer->AddProcessor(*pic.vaapi, index);
934 #endif
935 #ifdef HAS_LIBSTAGEFRIGHT
936   else if(pic.format == RENDER_FMT_EGLIMG)
937     m_pRenderer->AddProcessor(pic.stf, pic.eglimg, index);
938 #endif
939
940   m_pRenderer->ReleaseImage(index, false);
941
942   return index;
943 }
944
945 bool CXBMCRenderManager::Supports(ERENDERFEATURE feature)
946 {
947   CSharedLock lock(m_sharedSection);
948   if (m_pRenderer)
949     return m_pRenderer->Supports(feature);
950   else
951     return false;
952 }
953
954 bool CXBMCRenderManager::Supports(EDEINTERLACEMODE method)
955 {
956   CSharedLock lock(m_sharedSection);
957   if (m_pRenderer)
958     return m_pRenderer->Supports(method);
959   else
960     return false;
961 }
962
963 bool CXBMCRenderManager::Supports(EINTERLACEMETHOD method)
964 {
965   CSharedLock lock(m_sharedSection);
966   if (m_pRenderer)
967     return m_pRenderer->Supports(method);
968   else
969     return false;
970 }
971
972 bool CXBMCRenderManager::Supports(ESCALINGMETHOD method)
973 {
974   CSharedLock lock(m_sharedSection);
975   if (m_pRenderer)
976     return m_pRenderer->Supports(method);
977   else
978     return false;
979 }
980
981 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethod(EINTERLACEMETHOD mInt)
982 {
983   CSharedLock lock(m_sharedSection);
984   return AutoInterlaceMethodInternal(mInt);
985 }
986
987 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethodInternal(EINTERLACEMETHOD mInt)
988 {
989   if (mInt == VS_INTERLACEMETHOD_NONE)
990     return VS_INTERLACEMETHOD_NONE;
991
992   if(!m_pRenderer->Supports(mInt))
993     mInt = VS_INTERLACEMETHOD_AUTO;
994
995   if (mInt == VS_INTERLACEMETHOD_AUTO)
996     return m_pRenderer->AutoInterlaceMethod();
997
998   return mInt;
999 }
1000
1001 int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop, int timeout)
1002 {
1003   CSingleLock lock2(m_presentlock);
1004
1005   XbmcThreads::EndTime endtime(timeout);
1006   while(m_free.empty())
1007   {
1008     m_presentevent.wait(lock2, std::min(50, timeout));
1009     if(endtime.IsTimePast() || bStop)
1010     {
1011       if (timeout != 0 && !bStop)
1012         CLog::Log(LOGWARNING, "CRenderManager::WaitForBuffer - timeout waiting for buffer");
1013       return -1;
1014     }
1015   }
1016
1017   // make sure overlay buffer is released, this won't happen on AddOverlay
1018   m_overlays.Release(m_free.front());
1019
1020   // return buffer level
1021   return m_queued.size() + m_discard.size();;
1022 }
1023
1024 void CXBMCRenderManager::PrepareNextRender()
1025 {
1026   CSingleLock lock(m_presentlock);
1027
1028   if (m_queued.empty())
1029   {
1030     CLog::Log(LOGERROR, "CRenderManager::PrepareNextRender - asked to prepare with nothing available");
1031     m_presentstep = PRESENT_IDLE;
1032     m_presentevent.notifyAll();
1033     return;
1034   }
1035
1036   double clocktime = GetPresentTime();
1037   double frametime = 1.0 / GetMaximumFPS();
1038
1039   /* see if any future queued frames are already due */
1040   std::deque<int>::reverse_iterator curr, prev;
1041   int idx;
1042   curr = prev = m_queued.rbegin();
1043   ++prev;
1044   while (prev != m_queued.rend())
1045   {
1046     if(clocktime > m_Queue[*prev].timestamp                 /* previous frame is late */
1047     && clocktime > m_Queue[*curr].timestamp - frametime)    /* selected frame is close to it's display time */
1048       break;
1049     ++curr;
1050     ++prev;
1051   }
1052   idx = *curr;
1053
1054   /* in fullscreen we will block after render, but only for MAXPRESENTDELAY */
1055   bool next;
1056   if(g_graphicsContext.IsFullScreenVideo())
1057     next = (m_Queue[idx].timestamp <= clocktime + MAXPRESENTDELAY);
1058   else
1059     next = (m_Queue[idx].timestamp <= clocktime + frametime);
1060
1061   if (next)
1062   {
1063     /* skip late frames */
1064     while(m_queued.front() != idx)
1065     {
1066       requeue(m_discard, m_queued);
1067       m_QueueSkip++;
1068     }
1069
1070     m_presentstep   = PRESENT_FLIP;
1071     m_discard.push_back(m_presentsource);
1072     m_presentsource = idx;
1073     m_queued.pop_front();
1074     m_presentevent.notifyAll();
1075   }
1076 }
1077
1078 void CXBMCRenderManager::DiscardBuffer()
1079 {
1080   CSharedLock lock(m_sharedSection);
1081   CSingleLock lock2(m_presentlock);
1082
1083   while(!m_queued.empty())
1084     requeue(m_discard, m_queued);
1085
1086   if(m_presentstep == PRESENT_READY)
1087     m_presentstep   = PRESENT_IDLE;
1088   m_presentevent.notifyAll();
1089 }