1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "RenderManager.h"
10 
11 #include "Application.h"
12 #include "RenderCapture.h"
13 #include "RenderFactory.h"
14 #include "RenderFlags.h"
15 #include "ServiceBroker.h"
16 #include "cores/VideoPlayer/Interface/TimingConstants.h"
17 #include "messaging/ApplicationMessenger.h"
18 #include "settings/AdvancedSettings.h"
19 #include "settings/MediaSettings.h"
20 #include "settings/Settings.h"
21 #include "settings/SettingsComponent.h"
22 #include "threads/SingleLock.h"
23 #include "utils/MathUtils.h"
24 #include "utils/StringUtils.h"
25 #include "utils/XTimeUtils.h"
26 #include "utils/log.h"
27 #include "windowing/GraphicContext.h"
28 #include "windowing/WinSystem.h"
29 
30 /* to use the same as player */
31 #include "../VideoPlayer/DVDClock.h"
32 #include "../VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h"
33 
34 using namespace KODI::MESSAGING;
35 
Reset()36 void CRenderManager::CClockSync::Reset()
37 {
38   m_error = 0;
39   m_errCount = 0;
40   m_syncOffset = 0;
41   m_enabled = false;
42 }
43 
44 unsigned int CRenderManager::m_nextCaptureId = 0;
45 
CRenderManager(CDVDClock & clock,IRenderMsg * player)46 CRenderManager::CRenderManager(CDVDClock &clock, IRenderMsg *player) :
47   m_dvdClock(clock),
48   m_playerPort(player)
49 {
50 }
51 
~CRenderManager()52 CRenderManager::~CRenderManager()
53 {
54   delete m_pRenderer;
55 }
56 
GetVideoRect(CRect & source,CRect & dest,CRect & view)57 void CRenderManager::GetVideoRect(CRect &source, CRect &dest, CRect &view)
58 {
59   CSingleLock lock(m_statelock);
60   if (m_pRenderer)
61     m_pRenderer->GetVideoRect(source, dest, view);
62 }
63 
GetAspectRatio()64 float CRenderManager::GetAspectRatio()
65 {
66   CSingleLock lock(m_statelock);
67   if (m_pRenderer)
68     return m_pRenderer->GetAspectRatio();
69   else
70     return 1.0f;
71 }
72 
SetVideoSettings(CVideoSettings settings)73 void CRenderManager::SetVideoSettings(CVideoSettings settings)
74 {
75   CSingleLock lock(m_statelock);
76   if (m_pRenderer)
77   {
78     m_pRenderer->SetVideoSettings(settings);
79   }
80 }
81 
Configure(const VideoPicture & picture,float fps,unsigned int orientation,int buffers)82 bool CRenderManager::Configure(const VideoPicture& picture, float fps, unsigned int orientation, int buffers)
83 {
84 
85   // check if something has changed
86   {
87     CSingleLock lock(m_statelock);
88 
89     if (!m_bRenderGUI)
90       return true;
91 
92     if (m_width == picture.iWidth &&
93         m_height == picture.iHeight &&
94         m_dwidth == picture.iDisplayWidth &&
95         m_dheight == picture.iDisplayHeight &&
96         m_fps == fps &&
97         m_orientation == orientation &&
98         m_stereomode == picture.stereoMode &&
99         m_NumberBuffers == buffers &&
100         m_pRenderer != nullptr &&
101         !m_pRenderer->ConfigChanged(picture))
102     {
103       return true;
104     }
105   }
106 
107   CLog::Log(LOGDEBUG, "CRenderManager::Configure - change configuration. %dx%d. display: %dx%d. framerate: %4.2f.", picture.iWidth, picture.iHeight, picture.iDisplayWidth, picture.iDisplayHeight, fps);
108 
109   // make sure any queued frame was fully presented
110   {
111     CSingleLock lock(m_presentlock);
112     XbmcThreads::EndTime endtime(5000);
113     m_forceNext = true;
114     while (m_presentstep != PRESENT_IDLE)
115     {
116       if(endtime.IsTimePast())
117       {
118         CLog::Log(LOGWARNING, "CRenderManager::Configure - timeout waiting for state");
119         m_forceNext = false;
120         return false;
121       }
122       m_presentevent.wait(lock, endtime.MillisLeft());
123     }
124     m_forceNext = false;
125   }
126 
127   {
128     CSingleLock lock(m_statelock);
129     m_width = picture.iWidth;
130     m_height = picture.iHeight,
131     m_dwidth = picture.iDisplayWidth;
132     m_dheight = picture.iDisplayHeight;
133     m_fps = fps;
134     m_orientation = orientation;
135     m_stereomode = picture.stereoMode;
136     m_NumberBuffers  = buffers;
137     m_renderState = STATE_CONFIGURING;
138     m_stateEvent.Reset();
139     m_clockSync.Reset();
140     m_dvdClock.SetVsyncAdjust(0);
141     m_pConfigPicture.reset(new VideoPicture());
142     m_pConfigPicture->CopyRef(picture);
143 
144     CSingleLock lock2(m_presentlock);
145     m_presentstep = PRESENT_READY;
146     m_presentevent.notifyAll();
147   }
148 
149   if (!m_stateEvent.WaitMSec(1000))
150   {
151     CLog::Log(LOGWARNING, "CRenderManager::Configure - timeout waiting for configure");
152     CSingleLock lock(m_statelock);
153     return false;
154   }
155 
156   CSingleLock lock(m_statelock);
157   if (m_renderState != STATE_CONFIGURED)
158   {
159     CLog::Log(LOGWARNING, "CRenderManager::Configure - failed to configure");
160     return false;
161   }
162 
163   return true;
164 }
165 
Configure()166 bool CRenderManager::Configure()
167 {
168   // lock all interfaces
169   CSingleLock lock(m_statelock);
170   CSingleLock lock2(m_presentlock);
171   CSingleLock lock3(m_datalock);
172 
173   if (m_pRenderer)
174   {
175     DeleteRenderer();
176   }
177 
178   if (!m_pRenderer)
179   {
180     CreateRenderer();
181     if (!m_pRenderer)
182       return false;
183   }
184 
185   m_pRenderer->SetVideoSettings(m_playerPort->GetVideoSettings());
186   bool result = m_pRenderer->Configure(*m_pConfigPicture, m_fps, m_orientation);
187   if (result)
188   {
189     CRenderInfo info = m_pRenderer->GetRenderInfo();
190     int renderbuffers = info.max_buffer_size;
191     m_QueueSize = renderbuffers;
192     if (m_NumberBuffers > 0)
193       m_QueueSize = std::min(m_NumberBuffers, renderbuffers);
194 
195     if(m_QueueSize < 2)
196     {
197       m_QueueSize = 2;
198       CLog::Log(LOGWARNING, "CRenderManager::Configure - queue size too small (%d, %d, %d)", m_QueueSize, renderbuffers, m_NumberBuffers);
199     }
200 
201     m_pRenderer->SetBufferSize(m_QueueSize);
202     m_pRenderer->Update();
203 
204     m_playerPort->UpdateRenderInfo(info);
205     m_playerPort->UpdateGuiRender(true);
206     m_playerPort->UpdateVideoRender(!m_pRenderer->IsGuiLayer());
207 
208     m_queued.clear();
209     m_discard.clear();
210     m_free.clear();
211     m_presentsource = 0;
212     m_presentsourcePast = -1;
213     for (int i=1; i < m_QueueSize; i++)
214       m_free.push_back(i);
215 
216     m_bRenderGUI = true;
217     m_bTriggerUpdateResolution = true;
218     m_presentstep = PRESENT_IDLE;
219     m_presentpts = DVD_NOPTS_VALUE;
220     m_lateframes = -1;
221     m_presentevent.notifyAll();
222     m_renderedOverlay = false;
223     m_renderDebug = false;
224     m_clockSync.Reset();
225     m_dvdClock.SetVsyncAdjust(0);
226     m_overlays.SetStereoMode(m_stereomode);
227 
228     m_renderState = STATE_CONFIGURED;
229 
230     CLog::Log(LOGDEBUG, "CRenderManager::Configure - %d", m_QueueSize);
231   }
232   else
233     m_renderState = STATE_UNCONFIGURED;
234 
235   m_pConfigPicture.reset();
236 
237   m_stateEvent.Set();
238   m_playerPort->VideoParamsChange();
239   return result;
240 }
241 
IsConfigured() const242 bool CRenderManager::IsConfigured() const
243 {
244   CSingleLock lock(m_statelock);
245   if (m_renderState == STATE_CONFIGURED)
246     return true;
247   else
248     return false;
249 }
250 
ShowVideo(bool enable)251 void CRenderManager::ShowVideo(bool enable)
252 {
253   m_showVideo = enable;
254   if (!enable)
255     DiscardBuffer();
256 }
257 
FrameWait(int ms)258 void CRenderManager::FrameWait(int ms)
259 {
260   XbmcThreads::EndTime timeout(ms);
261   CSingleLock lock(m_presentlock);
262   while(m_presentstep == PRESENT_IDLE && !timeout.IsTimePast())
263     m_presentevent.wait(lock, timeout.MillisLeft());
264 }
265 
IsPresenting()266 bool CRenderManager::IsPresenting()
267 {
268   if (!IsConfigured())
269     return false;
270 
271   CSingleLock lock(m_presentlock);
272   if (!m_presentTimer.IsTimePast())
273     return true;
274   else
275     return false;
276 }
277 
FrameMove()278 void CRenderManager::FrameMove()
279 {
280   bool firstFrame = false;
281   UpdateResolution();
282 
283   {
284     CSingleLock lock(m_statelock);
285 
286     if (m_renderState == STATE_UNCONFIGURED)
287       return;
288     else if (m_renderState == STATE_CONFIGURING)
289     {
290       lock.Leave();
291       if (!Configure())
292         return;
293 
294       firstFrame = true;
295       FrameWait(50);
296     }
297 
298     CheckEnableClockSync();
299   }
300   {
301     CSingleLock lock2(m_presentlock);
302 
303     if (m_queued.empty())
304     {
305       m_presentstep = PRESENT_IDLE;
306     }
307     else
308     {
309       m_presentTimer.Set(1000);
310     }
311 
312     if (m_presentstep == PRESENT_READY)
313       PrepareNextRender();
314 
315     if (m_presentstep == PRESENT_FLIP)
316     {
317       m_presentstep = PRESENT_FRAME;
318       m_presentevent.notifyAll();
319     }
320 
321     // release all previous
322     for (std::deque<int>::iterator it = m_discard.begin(); it != m_discard.end(); )
323     {
324       // renderer may want to keep the frame for postprocessing
325       if (!m_pRenderer->NeedBuffer(*it) || !m_bRenderGUI)
326       {
327         m_pRenderer->ReleaseBuffer(*it);
328         m_overlays.Release(*it);
329         m_free.push_back(*it);
330         it = m_discard.erase(it);
331       }
332       else
333         ++it;
334     }
335 
336     m_playerPort->UpdateRenderBuffers(m_queued.size(), m_discard.size(), m_free.size());
337     m_bRenderGUI = true;
338   }
339 
340   m_playerPort->UpdateGuiRender(IsGuiLayer() || firstFrame);
341 
342   ManageCaptures();
343 }
344 
PreInit()345 void CRenderManager::PreInit()
346 {
347   {
348     CSingleLock lock(m_statelock);
349     if (m_renderState != STATE_UNCONFIGURED)
350       return;
351   }
352 
353   if (!g_application.IsCurrentThread())
354   {
355     m_initEvent.Reset();
356     CApplicationMessenger::GetInstance().PostMsg(TMSG_RENDERER_PREINIT);
357     if (!m_initEvent.WaitMSec(2000))
358     {
359       CLog::Log(LOGERROR, "%s - timed out waiting for renderer to preinit", __FUNCTION__);
360     }
361   }
362 
363   CSingleLock lock(m_statelock);
364 
365   if (!m_pRenderer)
366   {
367     CreateRenderer();
368   }
369 
370   UpdateLatencyTweak();
371 
372   m_QueueSize   = 2;
373   m_QueueSkip   = 0;
374   m_presentstep = PRESENT_IDLE;
375   m_bRenderGUI = true;
376 
377   m_initEvent.Set();
378 }
379 
UnInit()380 void CRenderManager::UnInit()
381 {
382   if (!g_application.IsCurrentThread())
383   {
384     m_initEvent.Reset();
385     CApplicationMessenger::GetInstance().PostMsg(TMSG_RENDERER_UNINIT);
386     if (!m_initEvent.WaitMSec(2000))
387     {
388       CLog::Log(LOGERROR, "%s - timed out waiting for renderer to uninit", __FUNCTION__);
389     }
390   }
391 
392   CSingleLock lock(m_statelock);
393 
394   m_overlays.Flush();
395   m_debugRenderer.Flush();
396 
397   DeleteRenderer();
398 
399   m_renderState = STATE_UNCONFIGURED;
400   m_width = 0;
401   m_height = 0;
402   m_bRenderGUI = false;
403   RemoveCaptures();
404 
405   m_initEvent.Set();
406 }
407 
Flush(bool wait,bool saveBuffers)408 bool CRenderManager::Flush(bool wait, bool saveBuffers)
409 {
410   if (!m_pRenderer)
411     return true;
412 
413   if (g_application.IsCurrentThread())
414   {
415     CLog::Log(LOGDEBUG, "%s - flushing renderer", __FUNCTION__);
416 
417 // fix deadlock on Windows only when is enabled 'Sync playback to display'
418 #ifndef TARGET_WINDOWS
419     CSingleExit exitlock(CServiceBroker::GetWinSystem()->GetGfxContext());
420 #endif
421 
422     CSingleLock lock(m_statelock);
423     CSingleLock lock2(m_presentlock);
424     CSingleLock lock3(m_datalock);
425 
426     if (m_pRenderer)
427     {
428       m_overlays.Flush();
429       m_debugRenderer.Flush();
430 
431       if (!m_pRenderer->Flush(saveBuffers))
432       {
433         m_queued.clear();
434         m_discard.clear();
435         m_free.clear();
436         m_presentsource = 0;
437         m_presentsourcePast = -1;
438         m_presentstep = PRESENT_IDLE;
439         for (int i = 1; i < m_QueueSize; i++)
440           m_free.push_back(i);
441       }
442 
443       m_flushEvent.Set();
444     }
445   }
446   else
447   {
448     m_flushEvent.Reset();
449     CApplicationMessenger::GetInstance().PostMsg(TMSG_RENDERER_FLUSH);
450     if (wait)
451     {
452       if (!m_flushEvent.WaitMSec(1000))
453       {
454         CLog::Log(LOGERROR, "%s - timed out waiting for renderer to flush", __FUNCTION__);
455         return false;
456       }
457       else
458         return true;
459     }
460   }
461   return true;
462 }
463 
CreateRenderer()464 void CRenderManager::CreateRenderer()
465 {
466   if (!m_pRenderer)
467   {
468     CVideoBuffer *buffer = nullptr;
469     if (m_pConfigPicture)
470       buffer = m_pConfigPicture->videoBuffer;
471 
472     auto renderers = VIDEOPLAYER::CRendererFactory::GetRenderers();
473     for (auto &id : renderers)
474     {
475       if (id == "default")
476         continue;
477 
478       m_pRenderer = VIDEOPLAYER::CRendererFactory::CreateRenderer(id, buffer);
479       if (m_pRenderer)
480       {
481         return;
482       }
483     }
484     m_pRenderer = VIDEOPLAYER::CRendererFactory::CreateRenderer("default", buffer);
485   }
486 }
487 
DeleteRenderer()488 void CRenderManager::DeleteRenderer()
489 {
490   if (m_pRenderer)
491   {
492     CLog::Log(LOGDEBUG, "%s - deleting renderer", __FUNCTION__);
493 
494     delete m_pRenderer;
495     m_pRenderer = NULL;
496   }
497 }
498 
AllocRenderCapture()499 unsigned int CRenderManager::AllocRenderCapture()
500 {
501   CRenderCapture *capture = new CRenderCapture;
502   m_captures[m_nextCaptureId] = capture;
503   return m_nextCaptureId++;
504 }
505 
ReleaseRenderCapture(unsigned int captureId)506 void CRenderManager::ReleaseRenderCapture(unsigned int captureId)
507 {
508   CSingleLock lock(m_captCritSect);
509 
510   std::map<unsigned int, CRenderCapture*>::iterator it;
511   it = m_captures.find(captureId);
512 
513   if (it != m_captures.end())
514     it->second->SetState(CAPTURESTATE_NEEDSDELETE);
515 }
516 
StartRenderCapture(unsigned int captureId,unsigned int width,unsigned int height,int flags)517 void CRenderManager::StartRenderCapture(unsigned int captureId, unsigned int width, unsigned int height, int flags)
518 {
519   CSingleLock lock(m_captCritSect);
520 
521   std::map<unsigned int, CRenderCapture*>::iterator it;
522   it = m_captures.find(captureId);
523   if (it == m_captures.end())
524   {
525     CLog::Log(LOGERROR, "CRenderManager::Capture - unknown capture id: %d", captureId);
526     return;
527   }
528 
529   CRenderCapture *capture = it->second;
530 
531   capture->SetState(CAPTURESTATE_NEEDSRENDER);
532   capture->SetUserState(CAPTURESTATE_WORKING);
533   capture->SetWidth(width);
534   capture->SetHeight(height);
535   capture->SetFlags(flags);
536   capture->GetEvent().Reset();
537 
538   if (g_application.IsCurrentThread())
539   {
540     if (flags & CAPTUREFLAG_IMMEDIATELY)
541     {
542       //render capture and read out immediately
543       RenderCapture(capture);
544       capture->SetUserState(capture->GetState());
545       capture->GetEvent().Set();
546     }
547   }
548 
549   if (!m_captures.empty())
550     m_hasCaptures = true;
551 }
552 
RenderCaptureGetPixels(unsigned int captureId,unsigned int millis,uint8_t * buffer,unsigned int size)553 bool CRenderManager::RenderCaptureGetPixels(unsigned int captureId, unsigned int millis, uint8_t *buffer, unsigned int size)
554 {
555   CSingleLock lock(m_captCritSect);
556 
557   std::map<unsigned int, CRenderCapture*>::iterator it;
558   it = m_captures.find(captureId);
559   if (it == m_captures.end())
560     return false;
561 
562   m_captureWaitCounter++;
563 
564   {
565     if (!millis)
566       millis = 1000;
567 
568     CSingleExit exitlock(m_captCritSect);
569     if (!it->second->GetEvent().WaitMSec(millis))
570     {
571       m_captureWaitCounter--;
572       return false;
573     }
574   }
575 
576   m_captureWaitCounter--;
577 
578   if (it->second->GetUserState() != CAPTURESTATE_DONE)
579     return false;
580 
581   unsigned int srcSize = it->second->GetWidth() * it->second->GetHeight() * 4;
582   unsigned int bytes = std::min(srcSize, size);
583 
584   memcpy(buffer, it->second->GetPixels(), bytes);
585   return true;
586 }
587 
ManageCaptures()588 void CRenderManager::ManageCaptures()
589 {
590   //no captures, return here so we don't do an unnecessary lock
591   if (!m_hasCaptures)
592     return;
593 
594   CSingleLock lock(m_captCritSect);
595 
596   std::map<unsigned int, CRenderCapture*>::iterator it = m_captures.begin();
597   while (it != m_captures.end())
598   {
599     CRenderCapture* capture = it->second;
600 
601     if (capture->GetState() == CAPTURESTATE_NEEDSDELETE)
602     {
603       delete capture;
604       it = m_captures.erase(it);
605       continue;
606     }
607 
608     if (capture->GetState() == CAPTURESTATE_NEEDSRENDER)
609       RenderCapture(capture);
610     else if (capture->GetState() == CAPTURESTATE_NEEDSREADOUT)
611       capture->ReadOut();
612 
613     if (capture->GetState() == CAPTURESTATE_DONE || capture->GetState() == CAPTURESTATE_FAILED)
614     {
615       //tell the thread that the capture is done or has failed
616       capture->SetUserState(capture->GetState());
617       capture->GetEvent().Set();
618 
619       if (capture->GetFlags() & CAPTUREFLAG_CONTINUOUS)
620       {
621         capture->SetState(CAPTURESTATE_NEEDSRENDER);
622 
623         //if rendering this capture continuously, and readout is async, render a new capture immediately
624         if (capture->IsAsync() && !(capture->GetFlags() & CAPTUREFLAG_IMMEDIATELY))
625           RenderCapture(capture);
626       }
627       ++it;
628     }
629     else
630     {
631       ++it;
632     }
633   }
634 
635   if (m_captures.empty())
636     m_hasCaptures = false;
637 }
638 
RenderCapture(CRenderCapture * capture)639 void CRenderManager::RenderCapture(CRenderCapture* capture)
640 {
641   if (!m_pRenderer || !m_pRenderer->RenderCapture(capture))
642     capture->SetState(CAPTURESTATE_FAILED);
643 }
644 
RemoveCaptures()645 void CRenderManager::RemoveCaptures()
646 {
647   CSingleLock lock(m_captCritSect);
648 
649   while (m_captureWaitCounter > 0)
650   {
651     for (auto entry : m_captures)
652     {
653       entry.second->GetEvent().Set();
654     }
655     CSingleExit lockexit(m_captCritSect);
656     KODI::TIME::Sleep(10);
657   }
658 
659   for (auto entry : m_captures)
660   {
661     delete entry.second;
662   }
663   m_captures.clear();
664 }
665 
SetViewMode(int iViewMode)666 void CRenderManager::SetViewMode(int iViewMode)
667 {
668   CSingleLock lock(m_statelock);
669   if (m_pRenderer)
670     m_pRenderer->SetViewMode(iViewMode);
671   m_playerPort->VideoParamsChange();
672 }
673 
GetResolution()674 RESOLUTION CRenderManager::GetResolution()
675 {
676   RESOLUTION res = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
677 
678   CSingleLock lock(m_statelock);
679   if (m_renderState == STATE_UNCONFIGURED)
680     return res;
681 
682   if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_OFF)
683     res = CResolutionUtils::ChooseBestResolution(m_fps, m_width, m_height, !m_stereomode.empty());
684 
685   return res;
686 }
687 
Render(bool clear,DWORD flags,DWORD alpha,bool gui)688 void CRenderManager::Render(bool clear, DWORD flags, DWORD alpha, bool gui)
689 {
690   CSingleExit exitLock(CServiceBroker::GetWinSystem()->GetGfxContext());
691 
692   {
693     CSingleLock lock(m_statelock);
694     if (m_renderState != STATE_CONFIGURED)
695       return;
696   }
697 
698   if (!gui && m_pRenderer->IsGuiLayer())
699     return;
700 
701   if (!gui || m_pRenderer->IsGuiLayer())
702   {
703     SPresent& m = m_Queue[m_presentsource];
704 
705     if( m.presentmethod == PRESENT_METHOD_BOB )
706       PresentFields(clear, flags, alpha);
707     else if( m.presentmethod == PRESENT_METHOD_BLEND )
708       PresentBlend(clear, flags, alpha);
709     else
710       PresentSingle(clear, flags, alpha);
711   }
712 
713   if (gui)
714   {
715     if (!m_pRenderer->IsGuiLayer())
716       m_pRenderer->Update();
717 
718     m_renderedOverlay = m_overlays.HasOverlay(m_presentsource);
719     CRect src, dst, view;
720     m_pRenderer->GetVideoRect(src, dst, view);
721     m_overlays.SetVideoRect(src, dst, view);
722     m_overlays.Render(m_presentsource);
723 
724     if (m_renderDebug)
725     {
726       if (m_renderDebugVideo)
727       {
728         DEBUG_INFO_VIDEO video = m_pRenderer->GetDebugInfo(m_presentsource);
729         DEBUG_INFO_RENDER render = CServiceBroker::GetWinSystem()->GetDebugInfo();
730 
731         m_debugRenderer.SetInfo(video, render);
732       }
733       else
734       {
735         DEBUG_INFO_PLAYER info;
736 
737         m_playerPort->GetDebugInfo(info.audio, info.video, info.player);
738 
739         double refreshrate, clockspeed;
740         int missedvblanks;
741         info.vsync =
742             StringUtils::Format("VSyncOff: %.1f latency: %.3f  ", m_clockSync.m_syncOffset / 1000,
743                                 DVD_TIME_TO_MSEC(m_displayLatency) / 1000.0f);
744         if (m_dvdClock.GetClockInfo(missedvblanks, clockspeed, refreshrate))
745         {
746           info.vsync += StringUtils::Format("VSync: refresh:%.3f missed:%i speed:%.3f%%",
747                                             refreshrate, missedvblanks, clockspeed * 100);
748         }
749 
750         m_debugRenderer.SetInfo(info);
751       }
752 
753       m_debugRenderer.Render(src, dst, view);
754 
755       m_debugTimer.Set(1000);
756       m_renderedOverlay = true;
757     }
758   }
759 
760 
761   SPresent& m = m_Queue[m_presentsource];
762 
763   { CSingleLock lock(m_presentlock);
764 
765     if (m_presentstep == PRESENT_FRAME)
766     {
767       if (m.presentmethod == PRESENT_METHOD_BOB)
768         m_presentstep = PRESENT_FRAME2;
769       else
770         m_presentstep = PRESENT_IDLE;
771     }
772     else if (m_presentstep == PRESENT_FRAME2)
773       m_presentstep = PRESENT_IDLE;
774 
775     if (m_presentstep == PRESENT_IDLE)
776     {
777       if (!m_queued.empty())
778         m_presentstep = PRESENT_READY;
779     }
780 
781     m_presentevent.notifyAll();
782   }
783 }
784 
IsGuiLayer()785 bool CRenderManager::IsGuiLayer()
786 {
787   { CSingleLock lock(m_statelock);
788 
789     if (!m_pRenderer)
790       return false;
791 
792     if ((m_pRenderer->IsGuiLayer() && IsPresenting()) ||
793         m_renderedOverlay || m_overlays.HasOverlay(m_presentsource))
794       return true;
795 
796     if (m_renderDebug && m_debugTimer.IsTimePast())
797       return true;
798   }
799   return false;
800 }
801 
IsVideoLayer()802 bool CRenderManager::IsVideoLayer()
803 {
804   { CSingleLock lock(m_statelock);
805 
806     if (!m_pRenderer)
807       return false;
808 
809     if (!m_pRenderer->IsGuiLayer())
810       return true;
811   }
812   return false;
813 }
814 
815 /* simple present method */
PresentSingle(bool clear,DWORD flags,DWORD alpha)816 void CRenderManager::PresentSingle(bool clear, DWORD flags, DWORD alpha)
817 {
818   SPresent& m = m_Queue[m_presentsource];
819 
820   if (m.presentfield == FS_BOT)
821     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_BOT, alpha);
822   else if (m.presentfield == FS_TOP)
823     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_TOP, alpha);
824   else
825     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags, alpha);
826 }
827 
828 /* new simpler method of handling interlaced material, *
829  * we just render the two fields right after eachother */
PresentFields(bool clear,DWORD flags,DWORD alpha)830 void CRenderManager::PresentFields(bool clear, DWORD flags, DWORD alpha)
831 {
832   SPresent& m = m_Queue[m_presentsource];
833 
834   if(m_presentstep == PRESENT_FRAME)
835   {
836     if( m.presentfield == FS_BOT)
837       m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD0, alpha);
838     else
839       m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD0, alpha);
840   }
841   else
842   {
843     if( m.presentfield == FS_TOP)
844       m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_FIELD1, alpha);
845     else
846       m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_FIELD1, alpha);
847   }
848 }
849 
PresentBlend(bool clear,DWORD flags,DWORD alpha)850 void CRenderManager::PresentBlend(bool clear, DWORD flags, DWORD alpha)
851 {
852   SPresent& m = m_Queue[m_presentsource];
853 
854   if( m.presentfield == FS_BOT )
855   {
856     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_BOT | RENDER_FLAG_NOOSD, alpha);
857     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, false, flags | RENDER_FLAG_TOP, alpha / 2);
858   }
859   else
860   {
861     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, clear, flags | RENDER_FLAG_TOP | RENDER_FLAG_NOOSD, alpha);
862     m_pRenderer->RenderUpdate(m_presentsource, m_presentsourcePast, false, flags | RENDER_FLAG_BOT, alpha / 2);
863   }
864 }
865 
UpdateLatencyTweak()866 void CRenderManager::UpdateLatencyTweak()
867 {
868   float fps = CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS();
869   float refresh = fps;
870   if (CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution() == RES_WINDOW)
871     refresh = 0; // No idea about refresh rate when windowed, just get the default latency
872   m_latencyTweak = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->GetLatencyTweak(refresh);
873 }
874 
UpdateResolution()875 void CRenderManager::UpdateResolution()
876 {
877   if (m_bTriggerUpdateResolution)
878   {
879     if (CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() && CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
880     {
881       if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_OFF && m_fps > 0.0f)
882       {
883         RESOLUTION res = CResolutionUtils::ChooseBestResolution(m_fps, m_width, m_height, !m_stereomode.empty());
884         CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(res, false);
885         UpdateLatencyTweak();
886         if (m_pRenderer)
887           m_pRenderer->Update();
888       }
889       m_bTriggerUpdateResolution = false;
890       m_playerPort->VideoParamsChange();
891     }
892   }
893 }
894 
TriggerUpdateResolution(float fps,int width,int height,std::string & stereomode)895 void CRenderManager::TriggerUpdateResolution(float fps, int width, int height, std::string &stereomode)
896 {
897   if (width)
898   {
899     m_fps = fps;
900     m_width = width;
901     m_height = height;
902     m_stereomode = stereomode;
903   }
904   m_bTriggerUpdateResolution = true;
905 }
906 
ToggleDebug()907 void CRenderManager::ToggleDebug()
908 {
909   m_renderDebug = !m_renderDebug;
910   m_debugTimer.SetExpired();
911   m_renderDebugVideo = false;
912 }
913 
ToggleDebugVideo()914 void CRenderManager::ToggleDebugVideo()
915 {
916   m_renderDebug = !m_renderDebug;
917   m_debugTimer.SetExpired();
918   m_renderDebugVideo = true;
919 }
920 
AddVideoPicture(const VideoPicture & picture,volatile std::atomic_bool & bStop,EINTERLACEMETHOD deintMethod,bool wait)921 bool CRenderManager::AddVideoPicture(const VideoPicture& picture, volatile std::atomic_bool& bStop, EINTERLACEMETHOD deintMethod, bool wait)
922 {
923   CSingleLock lock(m_presentlock);
924 
925   if (m_free.empty())
926     return false;
927 
928   int index = m_free.front();
929 
930   {
931     CSingleLock lock(m_datalock);
932     if (!m_pRenderer)
933       return false;
934 
935     m_pRenderer->AddVideoPicture(picture, index);
936   }
937 
938 
939   // set fieldsync if picture is interlaced
940   EFIELDSYNC displayField = FS_NONE;
941   if (picture.iFlags & DVP_FLAG_INTERLACED)
942   {
943     if (deintMethod != EINTERLACEMETHOD::VS_INTERLACEMETHOD_NONE)
944     {
945       if (picture.iFlags & DVP_FLAG_TOP_FIELD_FIRST)
946         displayField = FS_TOP;
947       else
948         displayField = FS_BOT;
949     }
950   }
951 
952   EPRESENTMETHOD presentmethod = PRESENT_METHOD_SINGLE;
953   if (deintMethod == VS_INTERLACEMETHOD_NONE)
954   {
955     presentmethod = PRESENT_METHOD_SINGLE;
956     displayField = FS_NONE;
957   }
958   else
959   {
960     if (displayField == FS_NONE)
961       presentmethod = PRESENT_METHOD_SINGLE;
962     else
963     {
964       if (deintMethod == VS_INTERLACEMETHOD_RENDER_BLEND)
965         presentmethod = PRESENT_METHOD_BLEND;
966       else if (deintMethod == VS_INTERLACEMETHOD_RENDER_BOB)
967         presentmethod = PRESENT_METHOD_BOB;
968       else
969       {
970         if (!m_pRenderer->WantsDoublePass())
971           presentmethod = PRESENT_METHOD_SINGLE;
972         else
973           presentmethod = PRESENT_METHOD_BOB;
974       }
975     }
976   }
977 
978 
979   SPresent& m = m_Queue[index];
980   m.presentfield = displayField;
981   m.presentmethod = presentmethod;
982   m.pts = picture.pts;
983   m_queued.push_back(m_free.front());
984   m_free.pop_front();
985   m_playerPort->UpdateRenderBuffers(m_queued.size(), m_discard.size(), m_free.size());
986 
987   // signal to any waiters to check state
988   if (m_presentstep == PRESENT_IDLE)
989   {
990     m_presentstep = PRESENT_READY;
991     m_presentevent.notifyAll();
992   }
993 
994   if (wait)
995   {
996     m_forceNext = true;
997     XbmcThreads::EndTime endtime(200);
998     while (m_presentstep == PRESENT_READY)
999     {
1000       m_presentevent.wait(lock, 20);
1001       if(endtime.IsTimePast() || bStop)
1002       {
1003         if (!bStop)
1004         {
1005           CLog::Log(LOGWARNING, "CRenderManager::AddVideoPicture - timeout waiting for render");
1006         }
1007         break;
1008       }
1009     }
1010     m_forceNext = false;
1011   }
1012 
1013   return true;
1014 }
1015 
AddOverlay(CDVDOverlay * o,double pts)1016 void CRenderManager::AddOverlay(CDVDOverlay* o, double pts)
1017 {
1018   int idx;
1019   { CSingleLock lock(m_presentlock);
1020     if (m_free.empty())
1021       return;
1022     idx = m_free.front();
1023   }
1024   CSingleLock lock(m_datalock);
1025   m_overlays.AddOverlay(o, pts, idx);
1026 }
1027 
Supports(ERENDERFEATURE feature)1028 bool CRenderManager::Supports(ERENDERFEATURE feature)
1029 {
1030   CSingleLock lock(m_statelock);
1031   if (m_pRenderer)
1032     return m_pRenderer->Supports(feature);
1033   else
1034     return false;
1035 }
1036 
Supports(ESCALINGMETHOD method)1037 bool CRenderManager::Supports(ESCALINGMETHOD method)
1038 {
1039   CSingleLock lock(m_statelock);
1040   if (m_pRenderer)
1041     return m_pRenderer->Supports(method);
1042   else
1043     return false;
1044 }
1045 
WaitForBuffer(volatile std::atomic_bool & bStop,int timeout)1046 int CRenderManager::WaitForBuffer(volatile std::atomic_bool&bStop, int timeout)
1047 {
1048   CSingleLock lock(m_presentlock);
1049 
1050   // check if gui is active and discard buffer if not
1051   // this keeps videoplayer going
1052   if (!m_bRenderGUI || !g_application.GetRenderGUI())
1053   {
1054     m_bRenderGUI = false;
1055     double presenttime = 0;
1056     double clock = m_dvdClock.GetClock();
1057     if (!m_queued.empty())
1058     {
1059       int idx = m_queued.front();
1060       presenttime = m_Queue[idx].pts;
1061     }
1062     else
1063       presenttime = clock + 0.02;
1064 
1065     int sleeptime = static_cast<int>((presenttime - clock) * 1000);
1066     if (sleeptime < 0)
1067       sleeptime = 0;
1068     sleeptime = std::min(sleeptime, 20);
1069     m_presentevent.wait(lock, sleeptime);
1070     DiscardBuffer();
1071     return 0;
1072   }
1073 
1074   XbmcThreads::EndTime endtime(timeout);
1075   while(m_free.empty())
1076   {
1077     m_presentevent.wait(lock, std::min(50, timeout));
1078     if (endtime.IsTimePast() || bStop)
1079     {
1080       return -1;
1081     }
1082   }
1083 
1084   // make sure overlay buffer is released, this won't happen on AddOverlay
1085   m_overlays.Release(m_free.front());
1086 
1087   // return buffer level
1088   return m_queued.size() + m_discard.size();
1089 }
1090 
PrepareNextRender()1091 void CRenderManager::PrepareNextRender()
1092 {
1093   if (m_queued.empty())
1094   {
1095     CLog::Log(LOGERROR, "CRenderManager::PrepareNextRender - asked to prepare with nothing available");
1096     m_presentstep = PRESENT_IDLE;
1097     m_presentevent.notifyAll();
1098     return;
1099   }
1100 
1101   if (!m_showVideo && !m_forceNext)
1102     return;
1103 
1104   double frameOnScreen = m_dvdClock.GetClock();
1105   double frametime = 1.0 / CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * DVD_TIME_BASE;
1106 
1107   m_displayLatency = DVD_MSEC_TO_TIME(m_latencyTweak + CServiceBroker::GetWinSystem()->GetGfxContext().GetDisplayLatency() - m_videoDelay - CServiceBroker::GetWinSystem()->GetFrameLatencyAdjustment());
1108 
1109   double renderPts = frameOnScreen + m_displayLatency;
1110 
1111   double nextFramePts = m_Queue[m_queued.front()].pts;
1112   if (m_dvdClock.GetClockSpeed() < 0)
1113     nextFramePts = renderPts;
1114 
1115   if (m_clockSync.m_enabled)
1116   {
1117     double err = fmod(renderPts - nextFramePts, frametime);
1118     m_clockSync.m_error += err;
1119     m_clockSync.m_errCount ++;
1120     if (m_clockSync.m_errCount > 30)
1121     {
1122       double average = m_clockSync.m_error / m_clockSync.m_errCount;
1123       m_clockSync.m_syncOffset = average;
1124       m_clockSync.m_error = 0;
1125       m_clockSync.m_errCount = 0;
1126 
1127       m_dvdClock.SetVsyncAdjust(-average);
1128     }
1129     renderPts += frametime / 2 - m_clockSync.m_syncOffset;
1130   }
1131   else
1132   {
1133     m_dvdClock.SetVsyncAdjust(0);
1134   }
1135 
1136   CLog::LogF(LOGDEBUG, LOGAVTIMING, "frameOnScreen: %f renderPts: %f nextFramePts: %f -> diff: %f  render: %u forceNext: %u", frameOnScreen, renderPts, nextFramePts, (renderPts - nextFramePts), renderPts >= nextFramePts, m_forceNext);
1137 
1138   bool combined = false;
1139   if (m_presentsourcePast >= 0)
1140   {
1141     m_discard.push_back(m_presentsourcePast);
1142     m_presentsourcePast = -1;
1143     combined = true;
1144   }
1145 
1146   if (renderPts >= nextFramePts || m_forceNext)
1147   {
1148     // see if any future queued frames are already due
1149     auto iter = m_queued.begin();
1150     int idx = *iter;
1151     ++iter;
1152     while (iter != m_queued.end())
1153     {
1154       // the slot for rendering in time is [pts .. (pts +  x * frametime)]
1155       // renderer/drivers have internal queues, being slightly late here does not mean that
1156       // we are really late. The likelihood that we recover decreases the greater m_lateframes
1157       // get. Skipping a frame is easier than having decoder dropping one (lateframes > 10)
1158       double x = (m_lateframes <= 6) ? 0.98 : 0;
1159       if (renderPts < m_Queue[*iter].pts + x * frametime)
1160         break;
1161       idx = *iter;
1162       ++iter;
1163     }
1164 
1165     // skip late frames
1166     while (m_queued.front() != idx)
1167     {
1168       if (m_presentsourcePast >= 0)
1169       {
1170         m_discard.push_back(m_presentsourcePast);
1171         m_QueueSkip++;
1172       }
1173       m_presentsourcePast = m_queued.front();
1174       m_queued.pop_front();
1175     }
1176 
1177     int lateframes = static_cast<int>((renderPts - m_Queue[idx].pts) * m_fps / DVD_TIME_BASE);
1178     if (lateframes)
1179       m_lateframes += lateframes;
1180     else
1181       m_lateframes = 0;
1182 
1183     m_presentstep = PRESENT_FLIP;
1184     m_discard.push_back(m_presentsource);
1185     m_presentsource = idx;
1186     m_queued.pop_front();
1187     m_presentpts = m_Queue[idx].pts - m_displayLatency;
1188     m_presentevent.notifyAll();
1189 
1190     m_playerPort->UpdateRenderBuffers(m_queued.size(), m_discard.size(), m_free.size());
1191   }
1192   else if (!combined && renderPts > (nextFramePts - frametime))
1193   {
1194     m_lateframes = 0;
1195     m_presentstep = PRESENT_FLIP;
1196     m_presentsourcePast = m_presentsource;
1197     m_presentsource = m_queued.front();
1198     m_queued.pop_front();
1199     m_presentpts = m_Queue[m_presentsource].pts - m_displayLatency - frametime / 2;
1200     m_presentevent.notifyAll();
1201   }
1202 }
1203 
DiscardBuffer()1204 void CRenderManager::DiscardBuffer()
1205 {
1206   CSingleLock lock2(m_presentlock);
1207 
1208   while(!m_queued.empty())
1209   {
1210     m_discard.push_back(m_queued.front());
1211     m_queued.pop_front();
1212   }
1213 
1214   if(m_presentstep == PRESENT_READY)
1215     m_presentstep = PRESENT_IDLE;
1216   m_presentevent.notifyAll();
1217 }
1218 
GetStats(int & lateframes,double & pts,int & queued,int & discard)1219 bool CRenderManager::GetStats(int &lateframes, double &pts, int &queued, int &discard)
1220 {
1221   CSingleLock lock(m_presentlock);
1222   lateframes = m_lateframes / 10;
1223   pts = m_presentpts;
1224   queued = m_queued.size();
1225   discard  = m_discard.size();
1226   return true;
1227 }
1228 
CheckEnableClockSync()1229 void CRenderManager::CheckEnableClockSync()
1230 {
1231   // refresh rate can be a multiple of video fps
1232   double diff = 1.0;
1233 
1234   if (m_fps != 0)
1235   {
1236     double fps = m_fps;
1237     double refreshrate, clockspeed;
1238     int missedvblanks;
1239     if (m_dvdClock.GetClockInfo(missedvblanks, clockspeed, refreshrate))
1240     {
1241       fps *= clockspeed;
1242     }
1243 
1244     diff = CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() / fps;
1245     if (diff < 1.0)
1246       diff = 1.0 / diff;
1247 
1248     // Calculate distance from nearest integer proportion
1249     diff = std::abs(std::round(diff) - diff);
1250   }
1251 
1252   if (diff < 0.0005)
1253   {
1254     m_clockSync.m_enabled = true;
1255   }
1256   else
1257   {
1258     m_clockSync.m_enabled = false;
1259     m_dvdClock.SetVsyncAdjust(0);
1260   }
1261 
1262   m_playerPort->UpdateClockSync(m_clockSync.m_enabled);
1263 }
1264