0bc206897dd86af2262d474f13c7d482ad63e5ef
[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   m_overlays.Render(m_presentsource);
768 }
769
770 /* simple present method */
771 void CXBMCRenderManager::PresentSingle(bool clear, DWORD flags, DWORD alpha)
772 {
773   CSingleLock lock(g_graphicsContext);
774
775   m_pRenderer->RenderUpdate(clear, flags, alpha);
776 }
777
778 /* new simpler method of handling interlaced material, *
779  * we just render the two fields right after eachother */
780 void CXBMCRenderManager::PresentFields(bool clear, DWORD flags, DWORD alpha)
781 {
782   CSingleLock lock(g_graphicsContext);
783   SPresent& m = m_Queue[m_presentsource];
784
785   if(m_presentstep == PRESENT_FRAME)
786   {
787     if( m.presentfield == FS_BOT)
788       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD0, alpha);
789     else
790       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD0, alpha);
791   }
792   else
793   {
794     if( m.presentfield == FS_TOP)
795       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD1, alpha);
796     else
797       m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD1, alpha);
798   }
799 }
800
801 void CXBMCRenderManager::PresentBlend(bool clear, DWORD flags, DWORD alpha)
802 {
803   CSingleLock lock(g_graphicsContext);
804   SPresent& m = m_Queue[m_presentsource];
805
806   if( m.presentfield == FS_BOT )
807   {
808     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_NOOSD, alpha);
809     m_pRenderer->RenderUpdate(false, flags | RENDER_FLAG_TOP, alpha / 2);
810   }
811   else
812   {
813     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_NOOSD, alpha);
814     m_pRenderer->RenderUpdate(false, flags | RENDER_FLAG_BOT, alpha / 2);
815   }
816 }
817
818 void CXBMCRenderManager::Recover()
819 {
820 #if defined(HAS_GL) && !defined(TARGET_DARWIN)
821   glFlush(); // attempt to have gpu done with pixmap and vdpau
822 #endif
823
824   UpdateDisplayLatency();
825 }
826
827 void CXBMCRenderManager::UpdateDisplayLatency()
828 {
829   float refresh = g_graphicsContext.GetFPS();
830   if (g_graphicsContext.GetVideoResolution() == RES_WINDOW)
831     refresh = 0; // No idea about refresh rate when windowed, just get the default latency
832   m_displayLatency = (double) g_advancedSettings.GetDisplayLatency(refresh);
833   CLog::Log(LOGDEBUG, "CRenderManager::UpdateDisplayLatency - Latency set to %1.0f msec", m_displayLatency * 1000.0f);
834 }
835
836 void CXBMCRenderManager::UpdateResolution()
837 {
838   if (m_bReconfigured)
839   {
840     CRetakeLock<CExclusiveLock> lock(m_sharedSection);
841     if (g_graphicsContext.IsFullScreenVideo() && g_graphicsContext.IsFullScreenRoot())
842     {
843       RESOLUTION res = GetResolution();
844       g_graphicsContext.SetVideoResolution(res);
845     }
846     m_bReconfigured = false;
847   }
848 }
849
850
851 unsigned int CXBMCRenderManager::GetProcessorSize()
852 {
853   CSharedLock lock(m_sharedSection);
854   return std::max(4, NUM_BUFFERS);
855 }
856
857 // Supported pixel formats, can be called before configure
858 std::vector<ERenderFormat> CXBMCRenderManager::SupportedFormats()
859 {
860   CSharedLock lock(m_sharedSection);
861   if (m_pRenderer)
862     return m_pRenderer->SupportedFormats();
863   return std::vector<ERenderFormat>();
864 }
865
866 int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
867 {
868   CSharedLock lock(m_sharedSection);
869   if (!m_pRenderer)
870     return -1;
871
872   int index;
873   {
874     CSingleLock lock(m_presentlock);
875     if (m_free.empty())
876       return -1;
877     index = m_free.front();
878   }
879
880   if(m_pRenderer->AddVideoPicture(&pic, index))
881     return 1;
882
883   YV12Image image;
884   if (m_pRenderer->GetImage(&image, index) < 0)
885     return -1;
886
887   if(pic.format == RENDER_FMT_YUV420P
888   || pic.format == RENDER_FMT_YUV420P10
889   || pic.format == RENDER_FMT_YUV420P16)
890   {
891     CDVDCodecUtils::CopyPicture(&image, &pic);
892   }
893   else if(pic.format == RENDER_FMT_NV12)
894   {
895     CDVDCodecUtils::CopyNV12Picture(&image, &pic);
896   }
897   else if(pic.format == RENDER_FMT_YUYV422
898        || pic.format == RENDER_FMT_UYVY422)
899   {
900     CDVDCodecUtils::CopyYUV422PackedPicture(&image, &pic);
901   }
902   else if(pic.format == RENDER_FMT_DXVA)
903   {
904     CDVDCodecUtils::CopyDXVA2Picture(&image, &pic);
905   }
906 #ifdef HAVE_LIBVDPAU
907   else if(pic.format == RENDER_FMT_VDPAU
908        || pic.format == RENDER_FMT_VDPAU_420)
909     m_pRenderer->AddProcessor(pic.vdpau, index);
910 #endif
911 #ifdef HAVE_LIBOPENMAX
912   else if(pic.format == RENDER_FMT_OMXEGL)
913     m_pRenderer->AddProcessor(pic.openMax, &pic, index);
914 #endif
915 #ifdef TARGET_DARWIN
916   else if(pic.format == RENDER_FMT_CVBREF)
917     m_pRenderer->AddProcessor(pic.cvBufferRef, index);
918 #endif
919 #ifdef HAVE_LIBVA
920   else if(pic.format == RENDER_FMT_VAAPI)
921     m_pRenderer->AddProcessor(*pic.vaapi, index);
922 #endif
923 #ifdef HAS_LIBSTAGEFRIGHT
924   else if(pic.format == RENDER_FMT_EGLIMG)
925     m_pRenderer->AddProcessor(pic.stf, pic.eglimg, index);
926 #endif
927 #if defined(TARGET_ANDROID)
928   else if(pic.format == RENDER_FMT_MEDIACODEC)
929     m_pRenderer->AddProcessor(pic.mediacodec, index);
930 #endif
931
932   m_pRenderer->ReleaseImage(index, false);
933
934   return index;
935 }
936
937 bool CXBMCRenderManager::Supports(ERENDERFEATURE feature)
938 {
939   CSharedLock lock(m_sharedSection);
940   if (m_pRenderer)
941     return m_pRenderer->Supports(feature);
942   else
943     return false;
944 }
945
946 bool CXBMCRenderManager::Supports(EDEINTERLACEMODE method)
947 {
948   CSharedLock lock(m_sharedSection);
949   if (m_pRenderer)
950     return m_pRenderer->Supports(method);
951   else
952     return false;
953 }
954
955 bool CXBMCRenderManager::Supports(EINTERLACEMETHOD method)
956 {
957   CSharedLock lock(m_sharedSection);
958   if (m_pRenderer)
959     return m_pRenderer->Supports(method);
960   else
961     return false;
962 }
963
964 bool CXBMCRenderManager::Supports(ESCALINGMETHOD 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 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethod(EINTERLACEMETHOD mInt)
974 {
975   CSharedLock lock(m_sharedSection);
976   return AutoInterlaceMethodInternal(mInt);
977 }
978
979 EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethodInternal(EINTERLACEMETHOD mInt)
980 {
981   if (mInt == VS_INTERLACEMETHOD_NONE)
982     return VS_INTERLACEMETHOD_NONE;
983
984   if(!m_pRenderer->Supports(mInt))
985     mInt = VS_INTERLACEMETHOD_AUTO;
986
987   if (mInt == VS_INTERLACEMETHOD_AUTO)
988     return m_pRenderer->AutoInterlaceMethod();
989
990   return mInt;
991 }
992
993 int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop, int timeout)
994 {
995   CSingleLock lock2(m_presentlock);
996
997   XbmcThreads::EndTime endtime(timeout);
998   while(m_free.empty())
999   {
1000     m_presentevent.wait(lock2, std::min(50, timeout));
1001     if(endtime.IsTimePast() || bStop)
1002     {
1003       if (timeout != 0 && !bStop)
1004         CLog::Log(LOGWARNING, "CRenderManager::WaitForBuffer - timeout waiting for buffer");
1005       return -1;
1006     }
1007   }
1008
1009   // make sure overlay buffer is released, this won't happen on AddOverlay
1010   m_overlays.Release(m_free.front());
1011
1012   // return buffer level
1013   return m_queued.size() + m_discard.size();;
1014 }
1015
1016 void CXBMCRenderManager::PrepareNextRender()
1017 {
1018   CSingleLock lock(m_presentlock);
1019
1020   if (m_queued.empty())
1021   {
1022     CLog::Log(LOGERROR, "CRenderManager::PrepareNextRender - asked to prepare with nothing available");
1023     m_presentstep = PRESENT_IDLE;
1024     m_presentevent.notifyAll();
1025     return;
1026   }
1027
1028   double clocktime = GetPresentTime();
1029   double frametime = 1.0 / GetMaximumFPS();
1030
1031   /* see if any future queued frames are already due */
1032   std::deque<int>::reverse_iterator curr, prev;
1033   int idx;
1034   curr = prev = m_queued.rbegin();
1035   ++prev;
1036   while (prev != m_queued.rend())
1037   {
1038     if(clocktime > m_Queue[*prev].timestamp                 /* previous frame is late */
1039     && clocktime > m_Queue[*curr].timestamp - frametime)    /* selected frame is close to it's display time */
1040       break;
1041     ++curr;
1042     ++prev;
1043   }
1044   idx = *curr;
1045
1046   /* in fullscreen we will block after render, but only for MAXPRESENTDELAY */
1047   bool next;
1048   if(g_graphicsContext.IsFullScreenVideo())
1049     next = (m_Queue[idx].timestamp <= clocktime + MAXPRESENTDELAY);
1050   else
1051     next = (m_Queue[idx].timestamp <= clocktime + frametime);
1052
1053   if (next)
1054   {
1055     /* skip late frames */
1056     while(m_queued.front() != idx)
1057     {
1058       requeue(m_discard, m_queued);
1059       m_QueueSkip++;
1060     }
1061
1062     m_presentstep   = PRESENT_FLIP;
1063     m_discard.push_back(m_presentsource);
1064     m_presentsource = idx;
1065     m_queued.pop_front();
1066     m_presentevent.notifyAll();
1067   }
1068 }
1069
1070 void CXBMCRenderManager::DiscardBuffer()
1071 {
1072   CSharedLock lock(m_sharedSection);
1073   CSingleLock lock2(m_presentlock);
1074
1075   while(!m_queued.empty())
1076     requeue(m_discard, m_queued);
1077
1078   if(m_presentstep == PRESENT_READY)
1079     m_presentstep   = PRESENT_IDLE;
1080   m_presentevent.notifyAll();
1081 }