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