changed: make bob deinterlacer use new method of presentstep to allow doubling of fps
[xbmc:xbmc-antiquated.git] / xbmc / cores / VideoRenderers / RenderManager.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21  
22 #include "stdafx.h"
23 #include "RenderManager.h"
24 #include "utils/CriticalSection.h"
25 #include "VideoReferenceClock.h"
26
27 #ifndef HAS_SDL
28 #include "PixelShaderRenderer.h"
29 #include "ComboRenderer.h"
30 #include "RGBRenderer.h"
31 #include "RGBRendererV2.h"
32 #endif
33 #include "Application.h"
34 #include "Settings.h"
35
36 #ifdef _LINUX
37 #include "PlatformInclude.h"
38 #endif
39
40 #ifdef HAS_SDL_OPENGL
41 #include "Application.h"
42 #include "LinuxRendererGL.h"
43 #else 
44 #include "LinuxRenderer.h"
45 #endif
46
47 #ifdef HAVE_LIBVDPAU
48 #include "cores/dvdplayer/DVDCodecs/Video/VDPAU.h"
49 #endif
50
51 /* to use the same as player */
52 #include "../dvdplayer/DVDClock.h"
53
54 CXBoxRenderManager g_renderManager;
55
56
57 #define MAXPRESENTDELAY 0.500
58
59 /* at any point we want an exclusive lock on rendermanager */
60 /* we must make sure we don't have a graphiccontext lock */
61 /* these two functions allow us to step out from that lock */
62 /* and reaquire it after having the exclusive lock */
63
64 #ifndef HAS_SDL
65 //VBlank information
66 HANDLE g_eventVBlank=NULL;
67 void VBlankCallback(D3DVBLANKDATA *pData)
68 {
69   PulseEvent(g_eventVBlank);
70 }
71 #endif
72
73 template<class T>
74 class CRetakeLock
75 {
76 public:
77   CRetakeLock(CSharedSection &section, bool immidiate = true, CCriticalSection &owned = g_graphicsContext)
78     : m_owned(owned)
79   {
80     m_count = ExitCriticalSection(m_owned);
81     m_lock  = new T(section);
82     if(immidiate)
83     {
84       RestoreCriticalSection(m_owned, m_count);
85       m_count = 0;
86     }
87   }
88   ~CRetakeLock()
89   {
90     delete m_lock;
91     RestoreCriticalSection(m_owned, m_count);
92   }
93   void Leave() { m_lock->Leave(); }
94   void Enter() { m_lock->Enter(); }
95
96 private:
97   T*                m_lock;
98   CCriticalSection &m_owned;
99   DWORD             m_count;
100 };
101
102 CXBoxRenderManager::CXBoxRenderManager()
103 {
104   m_pRenderer = NULL;
105   m_bPauseDrawing = false;
106   m_bIsStarted = false;
107
108   m_presentfield = FS_NONE;
109   m_presenttime = 0;
110   m_presentstep = 0;
111   m_rendermethod = 0;
112   m_presentmethod = VS_INTERLACEMETHOD_NONE;
113 }
114
115 CXBoxRenderManager::~CXBoxRenderManager()
116 {
117   delete m_pRenderer;
118   m_pRenderer = NULL;
119 }
120
121 /* These is based on QueryPerformanceCounter */
122 double CXBoxRenderManager::GetPresentTime()
123 {
124   return CDVDClock::GetAbsoluteClock() / DVD_TIME_BASE;
125 }
126
127 void CXBoxRenderManager::WaitPresentTime(double presenttime)
128 {
129   CDVDClock::WaitAbsoluteClock(presenttime * DVD_TIME_BASE);
130 }
131
132 bool CXBoxRenderManager::Configure(unsigned int width, unsigned int height, unsigned int d_width, unsigned int d_height, float fps, unsigned flags)
133 {
134   /* all frames before this should be rendered */
135   WaitPresentTime(m_presenttime);
136
137   CRetakeLock<CExclusiveLock> lock(m_sharedSection, false);
138
139   if(!m_pRenderer) 
140   {
141     CLog::Log(LOGERROR, "%s called without a valid Renderer object", __FUNCTION__);
142     return false;
143   }
144
145   bool result = m_pRenderer->Configure(width, height, d_width, d_height, fps, flags);
146   if(result)
147   {
148     if( flags & CONF_FLAGS_FULLSCREEN )
149     {
150       lock.Leave();
151       g_application.getApplicationMessenger().SwitchToFullscreen();
152       lock.Enter();
153     }
154     m_pRenderer->Update(false);
155     m_bIsStarted = true;
156   }
157   
158   return result;
159 }
160
161 bool CXBoxRenderManager::IsConfigured()
162 {
163   if (!m_pRenderer)
164     return false;
165   return m_pRenderer->IsConfigured();
166 }
167
168 void CXBoxRenderManager::Update(bool bPauseDrawing)
169 {
170   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
171
172   m_bPauseDrawing = bPauseDrawing;
173   if (m_pRenderer)
174   {
175     m_pRenderer->Update(bPauseDrawing);
176   }
177 }
178
179 void CXBoxRenderManager::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
180 {
181   CSharedLock lock(m_sharedSection);
182
183 #ifdef HAS_SDL_OPENGL  
184   if (m_pRenderer)
185     m_pRenderer->RenderUpdate(clear, flags | RENDER_FLAG_LAST, alpha);
186 #endif
187 }
188
189 unsigned int CXBoxRenderManager::PreInit()
190 {
191   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
192
193 #ifndef HAS_SDL
194   if(!g_eventVBlank)
195   {
196     //Only do this on first run
197     g_eventVBlank = CreateEvent(NULL,FALSE,FALSE,NULL);
198     D3DDevice::SetVerticalBlankCallback((D3DVBLANKCALLBACK)VBlankCallback);
199   }
200 #endif
201
202   m_bIsStarted = false;
203   m_bPauseDrawing = false;
204   if (!m_pRenderer)
205   { 
206 #ifndef HAS_SDL
207     // no renderer
208     m_rendermethod = g_guiSettings.GetInt("videoplayer.rendermethod");
209     if (m_rendermethod == RENDER_OVERLAYS)
210     {
211       CLog::Log(LOGDEBUG, __FUNCTION__" - Selected Overlay-Renderer");
212       m_pRenderer = new CComboRenderer(g_graphicsContext.Get3DDevice());
213     }
214     else if (m_rendermethod == RENDER_HQ_RGB_SHADER)
215     {
216       CLog::Log(LOGDEBUG, __FUNCTION__" - Selected RGB-Renderer");
217       m_pRenderer = new CRGBRenderer(g_graphicsContext.Get3DDevice());
218     }
219     else if (m_rendermethod == RENDER_HQ_RGB_SHADERV2)
220     {
221       CLog::Log(LOGDEBUG, __FUNCTION__" - Selected RGB-Renderer V2");
222       m_pRenderer = new CRGBRendererV2(g_graphicsContext.Get3DDevice());
223     }
224     else // if (g_guiSettings.GetInt("videoplayer.rendermethod") == RENDER_LQ_RGB_SHADER)
225     {
226       CLog::Log(LOGDEBUG, __FUNCTION__" - Selected LQShader-Renderer");
227       m_pRenderer = new CPixelShaderRenderer(g_graphicsContext.Get3DDevice());
228     }
229 #elif defined(HAS_SDL_OPENGL)
230     m_pRenderer = new CLinuxRendererGL();
231 #else
232     m_pRenderer = new CLinuxRenderer();
233 #endif
234   }
235
236   return m_pRenderer->PreInit();
237 }
238
239 void CXBoxRenderManager::UnInit()
240 {
241   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
242
243   m_bIsStarted = false;
244
245   // free renderer resources.
246   // TODO: we may also want to release the renderer here.
247   if (m_pRenderer)
248     m_pRenderer->UnInit();
249 }
250
251 void CXBoxRenderManager::SetupScreenshot()
252 {
253   CSharedLock lock(m_sharedSection);
254   if (m_pRenderer)
255     m_pRenderer->SetupScreenshot();
256 }
257
258 #ifndef HAS_SDL
259 void CXBoxRenderManager::CreateThumbnail(LPDIRECT3DSURFACE8 surface, unsigned int width, unsigned int height)
260 #else
261 void CXBoxRenderManager::CreateThumbnail(SDL_Surface * surface, unsigned int width, unsigned int height)
262 #endif
263 {
264   CSharedLock lock(m_sharedSection);
265   if (m_pRenderer)
266     m_pRenderer->CreateThumbnail(surface, width, height);
267 }
268
269 void CXBoxRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
270 {
271   if(timestamp - GetPresentTime() > MAXPRESENTDELAY)
272     timestamp =  GetPresentTime() + MAXPRESENTDELAY;
273
274   /* make sure any queued frame was presented */
275   if(g_graphicsContext.IsFullScreenVideo())
276     while(!g_application.WaitFrame(100) && !bStop) {}
277   else
278     WaitPresentTime(timestamp);
279
280   if(bStop)
281     return;
282
283   { CRetakeLock<CExclusiveLock> lock(m_sharedSection);
284     if(!m_pRenderer) return;
285
286     m_presenttime  = timestamp;
287     m_presentfield = sync;
288     m_presentstep  = 0;
289     m_presentmethod = g_stSettings.m_currentVideoSettings.m_InterlaceMethod;
290
291     /* select render method for auto */
292     if(m_presentmethod == VS_INTERLACEMETHOD_AUTO)
293     {
294       if(m_presentfield == FS_NONE)
295         m_presentmethod = VS_INTERLACEMETHOD_NONE;
296       else
297         m_presentmethod = VS_INTERLACEMETHOD_RENDER_BOB;
298     }
299
300     /* default to odd field if we want to deinterlace and don't know better */
301     if(m_presentfield == FS_NONE && m_presentmethod != VS_INTERLACEMETHOD_NONE)
302       m_presentfield = FS_ODD;
303
304     /* invert present field if we have one of those methods */
305     if( m_presentfield == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED 
306      || m_presentfield == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED )
307     {
308       if( m_presentfield == FS_EVEN )
309         m_presentfield = FS_ODD;
310       else
311         m_presentfield = FS_EVEN;
312     }
313
314     m_pRenderer->FlipPage(source);
315   }
316
317   m_presentevent.Reset();
318   g_application.NewFrame();
319   m_presentevent.WaitMSec(1); // we give the application thread 1ms to present
320 }
321
322 float CXBoxRenderManager::GetMaximumFPS()
323 {
324   float fps;
325
326   if (g_videoConfig.GetVSyncMode() != VSYNC_DISABLED)
327   {
328     fps = g_VideoReferenceClock.GetRefreshRate();
329     if (fps <= 0) fps = g_graphicsContext.GetFPS();
330   }
331   else
332     fps = 1000.0f;
333
334 #ifndef HAS_SDL
335   if( m_rendermethod == RENDER_HQ_RGB_SHADER
336    || m_rendermethod == RENDER_HQ_RGB_SHADERV2)
337   {
338     EINTERLACEMETHOD method = g_stSettings.m_currentVideoSettings.m_InterlaceMethod;
339
340     if((method == VS_INTERLACEMETHOD_AUTO && m_presentfield != FS_NONE)
341     ||  method == VS_INTERLACEMETHOD_RENDER_BOB )
342       fps *= 0.5;
343   }
344 #endif
345   
346   return fps;
347 }
348
349 bool CXBoxRenderManager::SupportsBrightness()
350 {
351   CSharedLock lock(m_sharedSection);
352   if (m_pRenderer)
353     return m_pRenderer->SupportsBrightness();
354   return false;
355 }
356
357 bool CXBoxRenderManager::SupportsContrast()
358 {
359   CSharedLock lock(m_sharedSection);
360   if (m_pRenderer)
361     return m_pRenderer->SupportsContrast();
362   return false;
363 }
364
365 bool CXBoxRenderManager::SupportsGamma()
366 {
367   CSharedLock lock(m_sharedSection);
368   if (m_pRenderer)
369     return m_pRenderer->SupportsGamma();
370   return false;
371 }
372
373 void CXBoxRenderManager::Present()
374 {
375   CSharedLock lock(m_sharedSection);
376
377 #ifdef HAVE_LIBVDPAU
378   /* wait for this present to be valid */
379   if(g_graphicsContext.IsFullScreenVideo() && g_VDPAU)
380     WaitPresentTime(m_presenttime);
381 #endif
382
383   if (!m_pRenderer)
384   {
385     CLog::Log(LOGERROR, "%s called without valid Renderer object", __FUNCTION__);
386     return;
387   }
388
389   if     ( m_presentmethod == VS_INTERLACEMETHOD_RENDER_BOB
390         || m_presentmethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED)
391     PresentBob();
392   else if( m_presentmethod == VS_INTERLACEMETHOD_RENDER_WEAVE
393         || m_presentmethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED)
394     PresentWeave();
395   else if( m_presentmethod == VS_INTERLACEMETHOD_RENDER_BLEND )
396     PresentBlend();
397   else
398     PresentSingle();
399
400   /* wait for this present to be valid */
401   if(g_graphicsContext.IsFullScreenVideo())
402   {
403     /* we are not done until present step has reverted to 0 */
404     if(m_presentstep == 0)
405       m_presentevent.Set();
406 #ifdef HAVE_LIBVDPAU
407     if (!g_VDPAU)
408 #endif
409     {
410       WaitPresentTime(m_presenttime);
411     }
412   }
413 }
414
415 /* simple present method */
416 void CXBoxRenderManager::PresentSingle()
417 {
418   CSingleLock lock(g_graphicsContext);
419
420   m_pRenderer->RenderUpdate(true, 0, 255);
421
422 #ifndef HAS_SDL
423   D3DDevice::Present( NULL, NULL, NULL, NULL );
424 #endif
425 }
426
427 /* new simpler method of handling interlaced material, *
428  * we just render the two fields right after eachother */
429 void CXBoxRenderManager::PresentBob()
430 {
431   CSingleLock lock(g_graphicsContext);
432
433   if(m_presentstep == 0)
434   {
435     if( m_presentfield == FS_EVEN)
436       m_pRenderer->RenderUpdate(true, RENDER_FLAG_EVEN, 255);
437     else
438       m_pRenderer->RenderUpdate(true, RENDER_FLAG_ODD, 255);
439     m_presentstep = 1;
440     g_application.NewFrame();
441   }
442   else
443   {
444     if( m_presentfield == FS_ODD)
445       m_pRenderer->RenderUpdate(true, RENDER_FLAG_EVEN, 255);
446     else
447       m_pRenderer->RenderUpdate(true, RENDER_FLAG_ODD, 255);
448     m_presentstep = 0;
449   }
450 #ifndef HAS_SDL
451   D3DDevice::Present( NULL, NULL, NULL, NULL );
452 #endif
453 }
454
455 void CXBoxRenderManager::PresentBlend()
456 {
457   CSingleLock lock(g_graphicsContext);
458
459   if( m_presentfield == FS_EVEN )
460   {
461     m_pRenderer->RenderUpdate(true, RENDER_FLAG_EVEN | RENDER_FLAG_NOOSD, 255);
462     m_pRenderer->RenderUpdate(false, RENDER_FLAG_ODD, 128);
463   }
464   else
465   {
466     m_pRenderer->RenderUpdate(true, RENDER_FLAG_ODD | RENDER_FLAG_NOOSD, 255);
467     m_pRenderer->RenderUpdate(false, RENDER_FLAG_EVEN, 128);
468   }
469
470 #ifndef HAS_SDL
471   D3DDevice::Present( NULL, NULL, NULL, NULL );
472 #endif
473 }
474
475 /* renders the two fields as one, but doing fieldbased *
476  * scaling then reinterlaceing resulting image         */
477 void CXBoxRenderManager::PresentWeave()
478 {
479   CSingleLock lock(g_graphicsContext);
480
481   m_pRenderer->RenderUpdate(true, RENDER_FLAG_BOTH, 255);
482
483 #ifndef HAS_SDL
484   //If we have interlaced video, we have to sync to only render on even fields
485   D3DFIELD_STATUS mStatus;
486   D3DDevice::GetDisplayFieldStatus(&mStatus);
487
488 #ifdef PROFILE
489   m_presentfield = FS_NONE;
490 #endif
491
492   //If this was not the correct field. we have to wait for the next one.. damn
493   if( (mStatus.Field == D3DFIELD_EVEN && m_presentfield == FS_EVEN) ||
494       (mStatus.Field == D3DFIELD_ODD && m_presentfield == FS_ODD) )
495   {
496     if( WaitForSingleObject(g_eventVBlank, 500) == WAIT_TIMEOUT )
497       CLog::Log(LOGERROR, __FUNCTION__" - Waiting for vertical-blank timed out");
498   }
499   D3DDevice::Present( NULL, NULL, NULL, NULL );
500 #endif
501 }
502
503 void CXBoxRenderManager::Recover()
504 {
505 #ifdef HAVE_LIBVDPAU
506   CRetakeLock<CExclusiveLock> lock(m_sharedSection);
507   if (g_VDPAU)
508   {
509     glFlush(); // attempt to have gpu done with pixmap
510     g_VDPAU->CheckRecover(true);
511   }
512 #endif
513 }