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