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