683272113307bf99c98d3f362a9e8be5f65cd9f3
[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   { CSingleLock lock(m_presentlock);
382
383     if(m_presentstep == PRESENT_FRAME)
384     {
385       if( m.presentmethod == PRESENT_METHOD_BOB
386       ||  m.presentmethod == PRESENT_METHOD_WEAVE)
387         m_presentstep = PRESENT_FRAME2;
388       else
389         m_presentstep = PRESENT_IDLE;
390     }
391     else if(m_presentstep == PRESENT_FRAME2)
392       m_presentstep = PRESENT_IDLE;
393
394
395     if(m_presentstep == PRESENT_IDLE)
396     {
397       if(!m_queued.empty())
398         m_presentstep = PRESENT_READY;
399     }
400
401     m_presentevent.notifyAll();
402   }
403 }
404
405 unsigned int CXBMCRenderManager::PreInit()
406 {
407   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
408
409   m_presentcorr = 0.0;
410   m_presenterr  = 0.0;
411   m_errorindex  = 0;
412   memset(m_errorbuff, 0, sizeof(m_errorbuff));
413
414   m_bIsStarted = false;
415   if (!m_pRenderer)
416   {
417 #if defined(HAS_GL)
418     m_pRenderer = new CLinuxRendererGL();
419 #elif HAS_GLES == 2
420     m_pRenderer = new CLinuxRendererGLES();
421 #elif defined(HAS_DX)
422     m_pRenderer = new CWinRenderer();
423 #elif defined(HAS_SDL)
424     m_pRenderer = new CLinuxRenderer();
425 #endif
426   }
427
428   UpdateDisplayLatency();
429
430   m_QueueSize   = 2;
431   m_QueueSkip   = 0;
432
433   return m_pRenderer->PreInit();
434 }
435
436 void CXBMCRenderManager::UnInit()
437 {
438   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
439
440   m_bIsStarted = false;
441
442   m_overlays.Flush();
443   g_fontManager.Unload("__subtitle__");
444   g_fontManager.Unload("__subtitleborder__");
445
446   // free renderer resources.
447   // TODO: we may also want to release the renderer here.
448   if (m_pRenderer)
449     m_pRenderer->UnInit();
450 }
451
452 bool CXBMCRenderManager::Flush()
453 {
454   if (!m_pRenderer)
455     return true;
456
457   if (g_application.IsCurrentThread())
458   {
459     CLog::Log(LOGDEBUG, "%s - flushing renderer", __FUNCTION__);
460
461     CRetakeLock<CExclusiveLock> lock(m_sharedSection);
462     m_pRenderer->Flush();
463     m_overlays.Flush();
464     m_flushEvent.Set();
465   }
466   else
467   {
468     ThreadMessage msg = {TMSG_RENDERER_FLUSH};
469     m_flushEvent.Reset();
470     CApplicationMessenger::Get().SendMessage(msg, false);
471     if (!m_flushEvent.WaitMSec(1000))
472     {
473       CLog::Log(LOGERROR, "%s - timed out waiting for renderer to flush", __FUNCTION__);
474       return false;
475     }
476     else
477       return true;
478   }
479   return true;
480 }
481
482 void CXBMCRenderManager::SetupScreenshot()
483 {
484   CSharedLock lock(m_sharedSection);
485   if (m_pRenderer)
486     m_pRenderer->SetupScreenshot();
487 }
488
489 CRenderCapture* CXBMCRenderManager::AllocRenderCapture()
490 {
491   return new CRenderCapture;
492 }
493
494 void CXBMCRenderManager::ReleaseRenderCapture(CRenderCapture* capture)
495 {
496   CSingleLock lock(m_captCritSect);
497
498   RemoveCapture(capture);
499
500   //because a CRenderCapture might have some gl things allocated, it can only be deleted from app thread
501   if (g_application.IsCurrentThread())
502   {
503     delete capture;
504   }
505   else
506   {
507     capture->SetState(CAPTURESTATE_NEEDSDELETE);
508     m_captures.push_back(capture);
509   }
510
511   if (!m_captures.empty())
512     m_hasCaptures = true;
513 }
514
515 void CXBMCRenderManager::Capture(CRenderCapture* capture, unsigned int width, unsigned int height, int flags)
516 {
517   CSingleLock lock(m_captCritSect);
518
519   RemoveCapture(capture);
520
521   capture->SetState(CAPTURESTATE_NEEDSRENDER);
522   capture->SetUserState(CAPTURESTATE_WORKING);
523   capture->SetWidth(width);
524   capture->SetHeight(height);
525   capture->SetFlags(flags);
526   capture->GetEvent().Reset();
527
528   if (g_application.IsCurrentThread())
529   {
530     if (flags & CAPTUREFLAG_IMMEDIATELY)
531     {
532       //render capture and read out immediately
533       RenderCapture(capture);
534       capture->SetUserState(capture->GetState());
535       capture->GetEvent().Set();
536     }
537
538     if ((flags & CAPTUREFLAG_CONTINUOUS) || !(flags & CAPTUREFLAG_IMMEDIATELY))
539     {
540       //schedule this capture for a render and readout
541       m_captures.push_back(capture);
542     }
543   }
544   else
545   {
546     //schedule this capture for a render and readout
547     m_captures.push_back(capture);
548   }
549
550   if (!m_captures.empty())
551     m_hasCaptures = true;
552 }
553
554 void CXBMCRenderManager::ManageCaptures()
555 {
556   //no captures, return here so we don't do an unnecessary lock
557   if (!m_hasCaptures)
558     return;
559
560   CSingleLock lock(m_captCritSect);
561
562   std::list<CRenderCapture*>::iterator it = m_captures.begin();
563   while (it != m_captures.end())
564   {
565     CRenderCapture* capture = *it;
566
567     if (capture->GetState() == CAPTURESTATE_NEEDSDELETE)
568     {
569       delete capture;
570       it = m_captures.erase(it);
571       continue;
572     }
573
574     if (capture->GetState() == CAPTURESTATE_NEEDSRENDER)
575       RenderCapture(capture);
576     else if (capture->GetState() == CAPTURESTATE_NEEDSREADOUT)
577       capture->ReadOut();
578
579     if (capture->GetState() == CAPTURESTATE_DONE || capture->GetState() == CAPTURESTATE_FAILED)
580     {
581       //tell the thread that the capture is done or has failed
582       capture->SetUserState(capture->GetState());
583       capture->GetEvent().Set();
584
585       if (capture->GetFlags() & CAPTUREFLAG_CONTINUOUS)
586       {
587         capture->SetState(CAPTURESTATE_NEEDSRENDER);
588
589         //if rendering this capture continuously, and readout is async, render a new capture immediately
590         if (capture->IsAsync() && !(capture->GetFlags() & CAPTUREFLAG_IMMEDIATELY))
591           RenderCapture(capture);
592
593         ++it;
594       }
595       else
596       {
597         it = m_captures.erase(it);
598       }
599     }
600     else
601     {
602       ++it;
603     }
604   }
605
606   if (m_captures.empty())
607     m_hasCaptures = false;
608 }
609
610 void CXBMCRenderManager::RenderCapture(CRenderCapture* capture)
611 {
612   CSharedLock lock(m_sharedSection);
613   if (!m_pRenderer || !m_pRenderer->RenderCapture(capture))
614     capture->SetState(CAPTURESTATE_FAILED);
615 }
616
617 void CXBMCRenderManager::RemoveCapture(CRenderCapture* capture)
618 {
619   //remove this CRenderCapture from the list
620   std::list<CRenderCapture*>::iterator it;
621   while ((it = find(m_captures.begin(), m_captures.end(), capture)) != m_captures.end())
622     m_captures.erase(it);
623 }
624
625 void CXBMCRenderManager::SetViewMode(int iViewMode)
626 {
627   CSharedLock lock(m_sharedSection);
628   if (m_pRenderer)
629     m_pRenderer->SetViewMode(iViewMode);
630 }
631
632 void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
633 {
634   { CSharedLock lock(m_sharedSection);
635
636     if(bStop)
637       return;
638
639     if(!m_pRenderer) return;
640
641     m_firstFlipPage = true;              // tempfix
642
643     EPRESENTMETHOD presentmethod;
644
645     EDEINTERLACEMODE deinterlacemode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
646     EINTERLACEMETHOD interlacemethod = AutoInterlaceMethodInternal(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
647
648     if(g_advancedSettings.m_videoDisableBackgroundDeinterlace && !g_graphicsContext.IsFullScreenVideo())
649       deinterlacemode = VS_DEINTERLACEMODE_OFF;
650
651     if (deinterlacemode == VS_DEINTERLACEMODE_OFF)
652       presentmethod = PRESENT_METHOD_SINGLE;
653     else
654     {
655       if (deinterlacemode == VS_DEINTERLACEMODE_AUTO && sync == FS_NONE)
656         presentmethod = PRESENT_METHOD_SINGLE;
657       else
658       {
659         bool invert = false;
660         if      (interlacemethod == VS_INTERLACEMETHOD_RENDER_BLEND)            presentmethod = PRESENT_METHOD_BLEND;
661         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE)            presentmethod = PRESENT_METHOD_WEAVE;
662         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED) { presentmethod = PRESENT_METHOD_WEAVE ; invert = true; }
663         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB)              presentmethod = PRESENT_METHOD_BOB;
664         else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED)   { presentmethod = PRESENT_METHOD_BOB; invert = true; }
665         else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BOB)                presentmethod = PRESENT_METHOD_BOB;
666         else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BEST)               presentmethod = PRESENT_METHOD_BOB;
667         else                                                                    presentmethod = PRESENT_METHOD_SINGLE;
668
669         /* default to odd field if we want to deinterlace and don't know better */
670         if (deinterlacemode == VS_DEINTERLACEMODE_FORCE && sync == FS_NONE)
671           sync = FS_TOP;
672
673         /* invert present field */
674         if(invert)
675         {
676           if( sync == FS_BOT )
677             sync = FS_TOP;
678           else
679             sync = FS_BOT;
680         }
681       }
682     }
683
684     /* failsafe for invalid timestamps, to make sure queue always empties */
685     if(timestamp > GetPresentTime() + 5.0)
686       timestamp = GetPresentTime() + 5.0;
687
688     CSingleLock lock2(m_presentlock);
689
690     if(m_free.empty())
691       return;
692
693     if(source < 0)
694       source = m_free.front();
695
696     SPresent& m = m_Queue[source];
697     m.timestamp     = timestamp;
698     m.presentfield  = sync;
699     m.presentmethod = presentmethod;
700     requeue(m_queued, m_free);
701
702     /* signal to any waiters to check state */
703     if(m_presentstep == PRESENT_IDLE)
704     {
705       m_presentstep = PRESENT_READY;
706       m_presentevent.notifyAll();
707     }
708   }
709 }
710
711 void CXBMCRenderManager::Reset()
712 {
713   CSharedLock lock(m_sharedSection);
714   if (m_pRenderer)
715     m_pRenderer->Reset();
716 }
717
718 RESOLUTION CXBMCRenderManager::GetResolution()
719 {
720   CSharedLock lock(m_sharedSection);
721   if (m_pRenderer)
722     return m_pRenderer->GetResolution();
723   else
724     return RES_INVALID;
725 }
726
727 float CXBMCRenderManager::GetMaximumFPS()
728 {
729   float fps;
730
731   if (CSettings::Get().GetInt("videoscreen.vsync") != VSYNC_DISABLED)
732   {
733     fps = (float)g_VideoReferenceClock.GetRefreshRate();
734     if (fps <= 0) fps = g_graphicsContext.GetFPS();
735   }
736   else
737     fps = 1000.0f;
738
739   return fps;
740 }
741
742 void CXBMCRenderManager::RegisterRenderUpdateCallBack(const void *ctx, RenderUpdateCallBackFn fn)
743 {
744   if (m_pRenderer)
745     m_pRenderer->RegisterRenderUpdateCallBack(ctx, fn);
746 }
747
748 void CXBMCRenderManager::RegisterRenderFeaturesCallBack(const void *ctx, RenderFeaturesCallBackFn fn)
749 {
750   if (m_pRenderer)
751     m_pRenderer->RegisterRenderFeaturesCallBack(ctx, fn);
752 }
753
754 void CXBMCRenderManager::Render(bool clear, DWORD flags, DWORD alpha)
755 {
756   CSharedLock lock(m_sharedSection);
757
758   SPresent& m = m_Queue[m_presentsource];
759
760   if( m.presentmethod == PRESENT_METHOD_BOB )
761     PresentFields(clear, flags, alpha);
762   else if( m.presentmethod == PRESENT_METHOD_WEAVE )
763     PresentFields(clear, flags | RENDER_FLAG_WEAVE, alpha);
764   else if( m.presentmethod == PRESENT_METHOD_BLEND )
765     PresentBlend(clear, flags, alpha);
766   else
767     PresentSingle(clear, flags, alpha);
768
769   m_overlays.Render(m_presentsource);
770 }
771
772 /* simple present method */
773 void CXBMCRenderManager::PresentSingle(bool clear, DWORD flags, DWORD alpha)
774 {
775   CSingleLock lock(g_graphicsContext);
776
777   m_pRenderer->RenderUpdate(clear, flags, alpha);
778 }
779
780 /* new simpler method of handling interlaced material, *
781  * we just render the two fields right after eachother */
782 void CXBMCRenderManager::PresentFields(bool clear, DWORD flags, DWORD alpha)
783 {
784   CSingleLock lock(g_graphicsContext);
785   SPresent& m = m_Queue[m_presentsource];
786
787   if(m_presentstep == PRESENT_FRAME)
788   {
789     if( m.presentfield == FS_BOT)
790       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD0, alpha);
791     else
792       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD0, alpha);
793   }
794   else
795   {
796     if( m.presentfield == FS_TOP)
797       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD1, alpha);
798     else
799       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD1, alpha);
800   }
801 }
802
803 void CXBMCRenderManager::PresentBlend(bool clear, DWORD flags, DWORD alpha)
804 {
805   CSingleLock lock(g_graphicsContext);
806   SPresent& m = m_Queue[m_presentsource];
807
808   if( m.presentfield == FS_BOT )
809   {
810     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_NOOSD, alpha);
811     m_pRenderer->RenderUpdate(false, flags | RENDER_FLAG_TOP, alpha / 2);
812   }
813   else
814   {
815     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_NOOSD, alpha);
816     m_pRenderer->RenderUpdate(false, flags | RENDER_FLAG_BOT, alpha / 2);
817   }
818 }
819
820 void CXBMCRenderManager::Recover()
821 {
822 #if defined(HAS_GL) && !defined(TARGET_DARWIN)
823   glFlush(); // attempt to have gpu done with pixmap and vdpau
824 #endif
825
826   UpdateDisplayLatency();
827 }
828
829 void CXBMCRenderManager::UpdateDisplayLatency()
830 {
831   float refresh = g_graphicsContext.GetFPS();
832   if (g_graphicsContext.GetVideoResolution() == RES_WINDOW)
833     refresh = 0; // No idea about refresh rate when windowed, just get the default latency
834   m_displayLatency = (double) g_advancedSettings.GetDisplayLatency(refresh);
835   CLog::Log(LOGDEBUG, "CRenderManager::UpdateDisplayLatency - Latency set to %1.0f msec", m_displayLatency * 1000.0f);
836 }
837
838 void CXBMCRenderManager::UpdateResolution()
839 {
840   if (m_bReconfigured)
841   {
842     CRetakeLock<CExclusiveLock> lock(m_sharedSection);
843     if (g_graphicsContext.IsFullScreenVideo() && g_graphicsContext.IsFullScreenRoot())
844     {
845       RESOLUTION res = GetResolution();
846       g_graphicsContext.SetVideoResolution(res);
847     }
848     m_bReconfigured = false;
849   }
850 }
851
852
853 unsigned int CXBMCRenderManager::GetProcessorSize()
854 {
855   CSharedLock lock(m_sharedSection);
856   return std::max(4, NUM_BUFFERS);
857 }
858
859 // Supported pixel formats, can be called before configure
860 std::vector<ERenderFormat> CXBMCRenderManager::SupportedFormats()
861 {
862   CSharedLock lock(m_sharedSection);
863   if (m_pRenderer)
864     return m_pRenderer->SupportedFormats();
865   return std::vector<ERenderFormat>();
866 }
867
868 int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
869 {
870   CSharedLock lock(m_sharedSection);
871   if (!m_pRenderer)
872     return -1;
873
874   int index;
875   {
876     CSingleLock lock(m_presentlock);
877     if (m_free.empty())
878       return -1;
879     index = m_free.front();
880   }
881
882   if(m_pRenderer->AddVideoPicture(&pic, index))
883     return 1;
884
885   YV12Image image;
886   if (m_pRenderer->GetImage(&image, index) < 0)
887     return -1;
888
889   if(pic.format == RENDER_FMT_YUV420P
890   || pic.format == RENDER_FMT_YUV420P10
891   || pic.format == RENDER_FMT_YUV420P16)
892   {
893     CDVDCodecUtils::CopyPicture(&image, &pic);
894   }
895   else if(pic.format == RENDER_FMT_NV12)
896   {
897     CDVDCodecUtils::CopyNV12Picture(&image, &pic);
898   }
899   else if(pic.format == RENDER_FMT_YUYV422
900        || pic.format == RENDER_FMT_UYVY422)
901   {
902     CDVDCodecUtils::CopyYUV422PackedPicture(&image, &pic);
903   }
904   else if(pic.format == RENDER_FMT_DXVA)
905   {
906     CDVDCodecUtils::CopyDXVA2Picture(&image, &pic);
907   }
908 #ifdef HAVE_LIBVDPAU
909   else if(pic.format == RENDER_FMT_VDPAU
910        || pic.format == RENDER_FMT_VDPAU_420)
911     m_pRenderer->AddProcessor(pic.vdpau, index);
912 #endif
913 #ifdef HAVE_LIBOPENMAX
914   else if(pic.format == RENDER_FMT_OMXEGL)
915     m_pRenderer->AddProcessor(pic.openMax, &pic, index);
916 #endif
917 #ifdef TARGET_DARWIN
918   else if(pic.format == RENDER_FMT_CVBREF)
919     m_pRenderer->AddProcessor(pic.cvBufferRef, index);
920 #endif
921 #ifdef HAVE_LIBVA
922   else if(pic.format == RENDER_FMT_VAAPI)
923     m_pRenderer->AddProcessor(*pic.vaapi, index);
924 #endif
925 #ifdef HAS_LIBSTAGEFRIGHT
926   else if(pic.format == RENDER_FMT_EGLIMG)
927     m_pRenderer->AddProcessor(pic.stf, pic.eglimg, index);
928 #endif
929 #if defined(TARGET_ANDROID)
930   else if(pic.format == RENDER_FMT_MEDIACODEC)
931     m_pRenderer->AddProcessor(pic.mediacodec, index);
932 #endif
933
934   m_pRenderer->ReleaseImage(index, false);
935
936   return index;
937 }
938
939 bool CXBMCRenderManager::Supports(ERENDERFEATURE feature)
940 {
941   CSharedLock lock(m_sharedSection);
942   if (m_pRenderer)
943     return m_pRenderer->Supports(feature);
944   else
945     return false;
946 }
947
948 bool CXBMCRenderManager::Supports(EDEINTERLACEMODE method)
949 {
950   CSharedLock lock(m_sharedSection);
951   if (m_pRenderer)
952     return m_pRenderer->Supports(method);
953   else
954     return false;
955 }
956
957 bool CXBMCRenderManager::Supports(EINTERLACEMETHOD method)
958 {
959   CSharedLock lock(m_sharedSection);
960   if (m_pRenderer)
961     return m_pRenderer->Supports(method);
962   else
963     return false;
964 }
965
966 bool CXBMCRenderManager::Supports(ESCALINGMETHOD method)
967 {
968   CSharedLock lock(m_sharedSection);
969   if (m_pRenderer)
970     return m_pRenderer->Supports(method);
971   else
972     return false;
973 }
974
975 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethod(EINTERLACEMETHOD mInt)
976 {
977   CSharedLock lock(m_sharedSection);
978   return AutoInterlaceMethodInternal(mInt);
979 }
980
981 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethodInternal(EINTERLACEMETHOD mInt)
982 {
983   if (mInt == VS_INTERLACEMETHOD_NONE)
984     return VS_INTERLACEMETHOD_NONE;
985
986   if(!m_pRenderer->Supports(mInt))
987     mInt = VS_INTERLACEMETHOD_AUTO;
988
989   if (mInt == VS_INTERLACEMETHOD_AUTO)
990     return m_pRenderer->AutoInterlaceMethod();
991
992   return mInt;
993 }
994
995 int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop, int timeout)
996 {
997   CSingleLock lock2(m_presentlock);
998
999   XbmcThreads::EndTime endtime(timeout);
1000   while(m_free.empty())
1001   {
1002     m_presentevent.wait(lock2, std::min(50, timeout));
1003     if(endtime.IsTimePast() || bStop)
1004     {
1005       if (timeout != 0 && !bStop)
1006         CLog::Log(LOGWARNING, "CRenderManager::WaitForBuffer - timeout waiting for buffer");
1007       return -1;
1008     }
1009   }
1010
1011   // make sure overlay buffer is released, this won't happen on AddOverlay
1012   m_overlays.Release(m_free.front());
1013
1014   // return buffer level
1015   return m_queued.size() + m_discard.size();;
1016 }
1017
1018 void CXBMCRenderManager::PrepareNextRender()
1019 {
1020   CSingleLock lock(m_presentlock);
1021
1022   if (m_queued.empty())
1023   {
1024     CLog::Log(LOGERROR, "CRenderManager::PrepareNextRender - asked to prepare with nothing available");
1025     m_presentstep = PRESENT_IDLE;
1026     m_presentevent.notifyAll();
1027     return;
1028   }
1029
1030   double clocktime = GetPresentTime();
1031   double frametime = 1.0 / GetMaximumFPS();
1032
1033   /* see if any future queued frames are already due */
1034   std::deque<int>::reverse_iterator curr, prev;
1035   int idx;
1036   curr = prev = m_queued.rbegin();
1037   ++prev;
1038   while (prev != m_queued.rend())
1039   {
1040     if(clocktime > m_Queue[*prev].timestamp                 /* previous frame is late */
1041     && clocktime > m_Queue[*curr].timestamp - frametime)    /* selected frame is close to it's display time */
1042       break;
1043     ++curr;
1044     ++prev;
1045   }
1046   idx = *curr;
1047
1048   /* in fullscreen we will block after render, but only for MAXPRESENTDELAY */
1049   bool next;
1050   if(g_graphicsContext.IsFullScreenVideo())
1051     next = (m_Queue[idx].timestamp <= clocktime + MAXPRESENTDELAY);
1052   else
1053     next = (m_Queue[idx].timestamp <= clocktime + frametime);
1054
1055   if (next)
1056   {
1057     /* skip late frames */
1058     while(m_queued.front() != idx)
1059     {
1060       requeue(m_discard, m_queued);
1061       m_QueueSkip++;
1062     }
1063
1064     m_presentstep   = PRESENT_FLIP;
1065     m_discard.push_back(m_presentsource);
1066     m_presentsource = idx;
1067     m_queued.pop_front();
1068     m_presentevent.notifyAll();
1069   }
1070 }
1071
1072 void CXBMCRenderManager::DiscardBuffer()
1073 {
1074   CSharedLock lock(m_sharedSection);
1075   CSingleLock lock2(m_presentlock);
1076
1077   while(!m_queued.empty())
1078     requeue(m_discard, m_queued);
1079
1080   if(m_presentstep == PRESENT_READY)
1081     m_presentstep   = PRESENT_IDLE;
1082   m_presentevent.notifyAll();
1083 }