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