[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / rendering / dx / RenderSystemDX.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21
22 #ifdef HAS_DX
23
24 #include "threads/SystemClock.h"
25 #include "RenderSystemDX.h"
26 #include "utils/log.h"
27 #include "utils/TimeUtils.h"
28 #include "utils/MathUtils.h"
29 #include "guilib/GUIWindowManager.h"
30 #include "threads/SingleLock.h"
31 #include "guilib/D3DResource.h"
32 #include "settings/AdvancedSettings.h"
33 #include "settings/Settings.h"
34 #include "utils/SystemInfo.h"
35 #include "Application.h"
36 #include "Util.h"
37 #include "win32/WIN32Util.h"
38 #include "video/VideoReferenceClock.h"
39 #if (D3DX_SDK_VERSION >= 42) //aug 2009 sdk and up use dxerr
40   #include <Dxerr.h>
41 #else
42   #include <dxerr9.h>
43   #define DXGetErrorString(hr)      DXGetErrorString9(hr)
44   #define DXGetErrorDescription(hr) DXGetErrorDescription9(hr)
45 #endif
46
47 using namespace std;
48
49 // Dynamic loading of Direct3DCreate9Ex to keep compatibility with 2000/XP.
50 typedef HRESULT (WINAPI *LPDIRECT3DCREATE9EX)( UINT SDKVersion, IDirect3D9Ex **ppD3D);
51 static LPDIRECT3DCREATE9EX g_Direct3DCreate9Ex;
52 static HMODULE             g_D3D9ExHandle;
53
54 static bool LoadD3D9Ex()
55 {
56   HMODULE hD3d9Dll =  GetModuleHandle("d3d9.dll");
57   if (!hD3d9Dll)
58     return false;
59   g_Direct3DCreate9Ex = (LPDIRECT3DCREATE9EX)GetProcAddress(hD3d9Dll, "Direct3DCreate9Ex" );
60   if(g_Direct3DCreate9Ex == NULL)
61     return false;
62   return true;
63 }
64
65 CRenderSystemDX::CRenderSystemDX() : CRenderSystemBase()
66 {
67   m_enumRenderingSystem = RENDERING_SYSTEM_DIRECTX;
68
69   m_pD3D        = NULL;
70   m_pD3DDevice  = NULL;
71   m_devType     = D3DDEVTYPE_HAL;
72 #if defined(DEBUG_PS) || defined (DEBUG_VS)
73     m_devType = D3DDEVTYPE_REF
74 #endif
75   m_hFocusWnd   = NULL;
76   m_hDeviceWnd  = NULL;
77   m_nBackBufferWidth  = 0;
78   m_nBackBufferHeight = 0;
79   m_bFullScreenDevice = false;
80   m_bVSync          = true;
81   m_nDeviceStatus   = S_OK;
82   m_stateBlock      = NULL;
83   m_inScene         = false;
84   m_needNewDevice   = false;
85   m_adapter         = D3DADAPTER_DEFAULT;
86   m_screenHeight    = 0;
87   m_systemFreq      = CurrentHostFrequency();
88   m_useD3D9Ex       = false;
89   m_defaultD3DUsage = 0;
90   m_defaultD3DPool  = D3DPOOL_MANAGED;
91
92   ZeroMemory(&m_D3DPP, sizeof(D3DPRESENT_PARAMETERS));
93 }
94
95 CRenderSystemDX::~CRenderSystemDX()
96 {
97 }
98
99 bool CRenderSystemDX::InitRenderSystem()
100 {
101   m_bVSync = true;
102
103   m_useD3D9Ex = (g_advancedSettings.m_AllowD3D9Ex && LoadD3D9Ex());
104   m_pD3D = NULL;
105
106   if (m_useD3D9Ex)
107   {
108     CLog::Log(LOGDEBUG, __FUNCTION__" - trying D3D9Ex...");
109     if (FAILED(g_Direct3DCreate9Ex(D3D_SDK_VERSION, (IDirect3D9Ex**) &m_pD3D)))
110     {
111       CLog::Log(LOGDEBUG, __FUNCTION__" - D3D9Ex creation failure, falling back to D3D9");
112       m_useD3D9Ex = false;
113     }
114     else
115     {
116       D3DCAPS9 caps;
117       memset(&caps, 0, sizeof(caps));
118       m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, m_devType, &caps);
119       // Evaluate if the driver is WDDM - this detection method is not guaranteed 100%
120       if (!g_advancedSettings.m_ForceD3D9Ex && (!(caps.Caps2 & D3DCAPS2_CANSHARERESOURCE) || !(caps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES)))
121       {
122         CLog::Log(LOGDEBUG, __FUNCTION__" - driver looks like XPDM or earlier, falling back to D3D9");
123         m_useD3D9Ex = false;
124         m_pD3D->Release();
125       }
126       else
127       {
128         CLog::Log(LOGDEBUG, __FUNCTION__" - using D3D9Ex");
129       }
130     }
131   }
132
133   if (!m_useD3D9Ex)
134   {
135     m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
136     if(m_pD3D == NULL)
137       return false;
138   }
139
140   UpdateMonitor();
141
142   if(CreateDevice()==false)
143     return false;
144
145   return true;
146 }
147
148 void CRenderSystemDX::SetRenderParams(unsigned int width, unsigned int height, bool fullScreen, float refreshRate)
149 {
150   m_nBackBufferWidth  = width;
151   m_nBackBufferHeight = height;
152   m_bFullScreenDevice = fullScreen;
153   m_refreshRate       = refreshRate;
154 }
155
156 void CRenderSystemDX::SetMonitor(HMONITOR monitor)
157 {
158   if (!m_pD3D)
159     return;
160
161   // find the appropriate screen
162   for (unsigned int adapter = 0; adapter < m_pD3D->GetAdapterCount(); adapter++)
163   {
164     HMONITOR hMonitor = m_pD3D->GetAdapterMonitor(adapter);
165     if (hMonitor == monitor && adapter != m_adapter)
166     {
167       m_adapter       = adapter;
168       m_needNewDevice = true;
169       break;
170     }
171   }
172 }
173
174 bool CRenderSystemDX::ResetRenderSystem(int width, int height, bool fullScreen, float refreshRate)
175 {
176   if (!m_pD3DDevice)
177     return false;
178
179   if (m_hDeviceWnd != NULL)
180   {
181     HMONITOR hMonitor = MonitorFromWindow(m_hDeviceWnd, MONITOR_DEFAULTTONULL);
182     if (hMonitor)
183       SetMonitor(hMonitor);
184   }
185
186   SetRenderParams(width, height, fullScreen, refreshRate);
187
188   CRect rc;
189   rc.SetRect(0, 0, (float)width, (float)height);
190   SetViewPort(rc);
191
192   BuildPresentParameters();
193
194   if (m_useD3D9Ex && !m_needNewDevice)
195     m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->ResetEx(&m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX);
196   else
197   {
198     OnDeviceLost();
199     OnDeviceReset();
200   }
201
202   return true;
203 }
204
205 void CRenderSystemDX::OnMove()
206 {
207   if (!m_bRenderCreated)
208     return;
209
210   HMONITOR currentMonitor = m_pD3D->GetAdapterMonitor(m_adapter);
211   HMONITOR newMonitor = MonitorFromWindow(m_hDeviceWnd, MONITOR_DEFAULTTONULL);
212   if (newMonitor != NULL && currentMonitor != newMonitor)
213     ResetRenderSystem(m_nBackBufferWidth, m_nBackBufferHeight, m_bFullScreenDevice, m_refreshRate);
214 }
215
216
217 bool CRenderSystemDX::IsSurfaceFormatOk(D3DFORMAT surfFormat, DWORD usage)
218 {
219   // Verify the compatibility
220   HRESULT hr = m_pD3D->CheckDeviceFormat(m_adapter,
221                                          m_devType,
222                                          m_D3DPP.BackBufferFormat,
223                                          usage,
224                                          D3DRTYPE_SURFACE,
225                                          surfFormat);
226
227   return (SUCCEEDED(hr)) ? true : false;
228 }
229
230 bool CRenderSystemDX::IsTextureFormatOk(D3DFORMAT texFormat, DWORD usage)
231 {
232   // Verify the compatibility
233   HRESULT hr = m_pD3D->CheckDeviceFormat(m_adapter,
234                                          m_devType,
235                                          m_D3DPP.BackBufferFormat,
236                                          usage,
237                                          D3DRTYPE_TEXTURE,
238                                          texFormat);
239
240   return (SUCCEEDED(hr)) ? true : false;
241 }
242
243 BOOL CRenderSystemDX::IsDepthFormatOk(D3DFORMAT DepthFormat, D3DFORMAT RenderTargetFormat)
244 {
245   // Verify that the depth format exists
246   if (!IsSurfaceFormatOk(DepthFormat, D3DUSAGE_DEPTHSTENCIL))
247     return false;
248
249   // Verify that the depth format is compatible
250   HRESULT hr = m_pD3D->CheckDepthStencilMatch(m_adapter,
251                                       m_devType,
252                                       m_D3DPP.BackBufferFormat,
253                                       RenderTargetFormat,
254                                       DepthFormat);
255
256   return SUCCEEDED(hr);
257 }
258
259 void CRenderSystemDX::BuildPresentParameters()
260 {
261   OSVERSIONINFOEX osvi;
262   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
263   osvi.dwOSVersionInfoSize = sizeof(osvi);
264   GetVersionEx((OSVERSIONINFO *)&osvi);
265
266   ZeroMemory( &m_D3DPP, sizeof(D3DPRESENT_PARAMETERS) );
267   m_D3DPP.Windowed           = m_useWindowedDX;
268   m_D3DPP.SwapEffect         = D3DSWAPEFFECT_FLIP;
269   m_D3DPP.BackBufferCount    = 2;
270
271   if(m_useD3D9Ex && (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 1 || osvi.dwMajorVersion > 6))
272   {
273 #if D3DX_SDK_VERSION >= 42
274     //m_D3DPP.SwapEffect       = D3DSWAPEFFECT_FLIPEX;
275 #else
276 #   pragma message("D3D SDK version is too old to support D3DSWAPEFFECT_FLIPEX")
277     CLog::Log(LOGWARNING, "CRenderSystemDX::BuildPresentParameters - xbmc compiled with an d3d sdk not supporting D3DSWAPEFFECT_FLIPEX");
278 #endif
279   }
280
281   m_D3DPP.hDeviceWindow      = m_hDeviceWnd;
282   m_D3DPP.BackBufferWidth    = m_nBackBufferWidth;
283   m_D3DPP.BackBufferHeight   = m_nBackBufferHeight;
284   m_D3DPP.Flags              = D3DPRESENTFLAG_VIDEO;
285   m_D3DPP.PresentationInterval = (m_bVSync) ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
286   m_D3DPP.FullScreen_RefreshRateInHz = (m_useWindowedDX) ? 0 : (int)m_refreshRate;
287   m_D3DPP.BackBufferFormat   = D3DFMT_X8R8G8B8;
288   m_D3DPP.MultiSampleType    = D3DMULTISAMPLE_NONE;
289   m_D3DPP.MultiSampleQuality = 0;
290
291   D3DFORMAT zFormat = D3DFMT_D16;
292   if      (IsDepthFormatOk(D3DFMT_D32, m_D3DPP.BackBufferFormat))           zFormat = D3DFMT_D32;
293   else if (IsDepthFormatOk(D3DFMT_D24S8, m_D3DPP.BackBufferFormat))         zFormat = D3DFMT_D24S8;
294   else if (IsDepthFormatOk(D3DFMT_D24X4S4, m_D3DPP.BackBufferFormat))       zFormat = D3DFMT_D24X4S4;
295   else if (IsDepthFormatOk(D3DFMT_D24X8, m_D3DPP.BackBufferFormat))         zFormat = D3DFMT_D24X8;
296   else if (IsDepthFormatOk(D3DFMT_D16, m_D3DPP.BackBufferFormat))           zFormat = D3DFMT_D16;
297   else if (IsDepthFormatOk(D3DFMT_D15S1, m_D3DPP.BackBufferFormat))         zFormat = D3DFMT_D15S1;
298
299   m_D3DPP.EnableAutoDepthStencil = TRUE;
300   m_D3DPP.AutoDepthStencilFormat = zFormat;
301
302   if (m_useD3D9Ex)
303   {
304     ZeroMemory( &m_D3DDMEX, sizeof(D3DDISPLAYMODEEX) );
305     m_D3DDMEX.Size             = sizeof(D3DDISPLAYMODEEX);
306     m_D3DDMEX.Width            = m_D3DPP.BackBufferWidth;
307     m_D3DDMEX.Height           = m_D3DPP.BackBufferHeight;
308     m_D3DDMEX.RefreshRate      = m_D3DPP.FullScreen_RefreshRateInHz;
309     m_D3DDMEX.Format           = m_D3DPP.BackBufferFormat;
310     m_D3DDMEX.ScanLineOrdering = m_interlaced ? D3DSCANLINEORDERING_INTERLACED : D3DSCANLINEORDERING_PROGRESSIVE;
311   }
312 }
313
314 bool CRenderSystemDX::DestroyRenderSystem()
315 {
316   DeleteDevice();
317
318   SAFE_RELEASE(m_stateBlock);
319   SAFE_RELEASE(m_pD3D);
320   SAFE_RELEASE(m_pD3DDevice);
321
322   m_bRenderCreated = false;
323
324   return true;
325 }
326
327 void CRenderSystemDX::DeleteDevice()
328 {
329   CSingleLock lock(m_resourceSection);
330
331   // tell any shared resources
332   for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
333     (*i)->OnDestroyDevice();
334
335   SAFE_RELEASE(m_pD3DDevice);
336   m_bRenderCreated = false;
337 }
338
339 void CRenderSystemDX::OnDeviceLost()
340 {
341   CSingleLock lock(m_resourceSection);
342   g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_LOST);
343   SAFE_RELEASE(m_stateBlock);
344
345   if (m_needNewDevice)
346     DeleteDevice();
347   else
348   {
349     // just resetting the device
350     for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
351       (*i)->OnLostDevice();
352   }
353 }
354
355 void CRenderSystemDX::OnDeviceReset()
356 {
357   CSingleLock lock(m_resourceSection);
358
359   if (m_needNewDevice)
360     CreateDevice();
361   else
362   {
363     // just need a reset
364     if (m_useD3D9Ex)
365       m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->ResetEx(&m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX);
366     else
367       m_nDeviceStatus = m_pD3DDevice->Reset(&m_D3DPP);
368   }
369
370   if (m_nDeviceStatus == S_OK)
371   { // we're back
372     for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
373       (*i)->OnResetDevice();
374
375     g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
376   }
377   else
378   {
379     for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
380       (*i)->OnLostDevice();
381   }
382 }
383
384 bool CRenderSystemDX::CreateDevice()
385 {
386   // Code based on Ogre 3D engine
387   CSingleLock lock(m_resourceSection);
388
389   HRESULT hr;
390
391   if(m_pD3D == NULL)
392     return false;
393
394   if(m_hDeviceWnd == NULL)
395     return false;
396
397   CLog::Log(LOGDEBUG, __FUNCTION__" on adapter %d", m_adapter);
398
399   BuildPresentParameters();
400
401   D3DCAPS9 caps;
402   memset(&caps, 0, sizeof(caps));
403   m_pD3D->GetDeviceCaps(m_adapter, m_devType, &caps);
404
405   DWORD VertexProcessingFlags = 0;
406   if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
407   {
408     /* Activate when the state management of the fixed pipeline is in order,
409       to get a bit more performance
410     if (caps.DevCaps & D3DDEVCAPS_PUREDEVICE)
411       VertexProcessingFlags = D3DCREATE_PUREDEVICE;
412     */
413     VertexProcessingFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
414     CLog::Log(LOGDEBUG, __FUNCTION__" - using hardware vertex processing");
415   }
416   else
417   {
418     VertexProcessingFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
419     CLog::Log(LOGDEBUG, __FUNCTION__" - using software vertex processing");
420   }
421
422   if (m_useD3D9Ex)
423   {
424     hr = ((IDirect3D9Ex*)m_pD3D)->CreateDeviceEx(m_adapter, m_devType, m_hFocusWnd,
425       VertexProcessingFlags | D3DCREATE_MULTITHREADED, &m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX, (IDirect3DDevice9Ex**)&m_pD3DDevice );
426     if (FAILED(hr))
427     {
428       CLog::Log(LOGWARNING, __FUNCTION__" - initial wanted device config failed");
429       // Try a second time, may fail the first time due to back buffer count,
430       // which will be corrected down to 1 by the runtime
431       hr = ((IDirect3D9Ex*)m_pD3D)->CreateDeviceEx( m_adapter, m_devType, m_hFocusWnd,
432         VertexProcessingFlags | D3DCREATE_MULTITHREADED, &m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX, (IDirect3DDevice9Ex**)&m_pD3DDevice );
433       if( FAILED( hr ) )
434       {
435         CLog::Log(LOGERROR, __FUNCTION__" - unable to create a device. %s", GetErrorDescription(hr).c_str());
436         return false;
437       }
438     }
439     // Not sure the following actually does something
440     ((IDirect3DDevice9Ex*)m_pD3DDevice)->SetGPUThreadPriority(7);
441   }
442   else
443   {
444     hr = m_pD3D->CreateDevice(m_adapter, m_devType, m_hFocusWnd,
445       VertexProcessingFlags | D3DCREATE_MULTITHREADED, &m_D3DPP, &m_pD3DDevice );
446     if (FAILED(hr))
447     {
448       CLog::Log(LOGWARNING, __FUNCTION__" - initial wanted device config failed");
449       // Try a second time, may fail the first time due to back buffer count,
450       // which will be corrected down to 1 by the runtime
451       hr = m_pD3D->CreateDevice( m_adapter, m_devType, m_hFocusWnd,
452         VertexProcessingFlags | D3DCREATE_MULTITHREADED, &m_D3DPP, &m_pD3DDevice );
453       if( FAILED( hr ) )
454       {
455         CLog::Log(LOGERROR, __FUNCTION__" - unable to create a device. %s", GetErrorDescription(hr).c_str());
456         return false;
457       }
458     }
459   }
460
461   if(m_pD3D->GetAdapterIdentifier(m_adapter, 0, &m_AIdentifier) == D3D_OK)
462   {
463     m_RenderRenderer = (const char*)m_AIdentifier.Description;
464     m_RenderVendor   = (const char*)m_AIdentifier.Driver;
465     m_RenderVersion = StringUtils::Format("%d.%d.%d.%04d", HIWORD(m_AIdentifier.DriverVersion.HighPart), LOWORD(m_AIdentifier.DriverVersion.HighPart),
466                                                            HIWORD(m_AIdentifier.DriverVersion.LowPart) , LOWORD(m_AIdentifier.DriverVersion.LowPart));
467   }
468
469   CLog::Log(LOGDEBUG, __FUNCTION__" - adapter %d: %s, %s, VendorId %lu, DeviceId %lu",
470             m_adapter, m_AIdentifier.Driver, m_AIdentifier.Description, m_AIdentifier.VendorId, m_AIdentifier.DeviceId);
471
472   // get our render capabilities
473   // re-read caps, there may be changes depending on the vertex processing type
474   m_pD3DDevice->GetDeviceCaps(&caps);
475
476   m_maxTextureSize = min(caps.MaxTextureWidth, caps.MaxTextureHeight);
477
478   if (g_advancedSettings.m_AllowDynamicTextures && m_useD3D9Ex && (caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES))
479   {
480     m_defaultD3DUsage = D3DUSAGE_DYNAMIC;
481     m_defaultD3DPool  = D3DPOOL_DEFAULT;
482     CLog::Log(LOGDEBUG, __FUNCTION__" - using D3DCAPS2_DYNAMICTEXTURES");
483   }
484   else
485   {
486     m_defaultD3DUsage = 0;
487     m_defaultD3DPool  = D3DPOOL_MANAGED;
488   }
489
490   m_renderCaps = 0;
491
492   CLog::Log(LOGDEBUG, __FUNCTION__" - texture caps: 0x%08X", caps.TextureCaps);
493
494   if(IsTextureFormatOk(D3DFMT_DXT1, m_defaultD3DUsage)
495   && IsTextureFormatOk(D3DFMT_DXT3, m_defaultD3DUsage)
496   && IsTextureFormatOk(D3DFMT_DXT5, m_defaultD3DUsage))
497     m_renderCaps |= RENDER_CAPS_DXT;
498
499   if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) == 0)
500   { // we're allowed NPOT textures
501     m_renderCaps |= RENDER_CAPS_NPOT;
502     if (((m_renderCaps & RENDER_CAPS_DXT) != 0) && ((caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) == 0))
503       m_renderCaps |= RENDER_CAPS_DXT_NPOT;
504   }
505   else if ((caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) != 0)
506   { // we're allowed _some_ NPOT textures (namely non-DXT and only with D3DTADDRESS_CLAMP and no wrapping)
507     m_renderCaps |= RENDER_CAPS_NPOT;
508   }
509
510   // Temporary - allow limiting the caps to debug a texture problem
511   if (g_advancedSettings.m_RestrictCapsMask != 0)
512     m_renderCaps &= ~g_advancedSettings.m_RestrictCapsMask;
513
514   if (m_renderCaps & RENDER_CAPS_DXT)
515     CLog::Log(LOGDEBUG, __FUNCTION__" - RENDER_CAPS_DXT");
516   if (m_renderCaps & RENDER_CAPS_NPOT)
517     CLog::Log(LOGDEBUG, __FUNCTION__" - RENDER_CAPS_NPOT");
518   if (m_renderCaps & RENDER_CAPS_DXT_NPOT)
519     CLog::Log(LOGDEBUG, __FUNCTION__" - RENDER_CAPS_DXT_NPOT");
520
521   // nVidia quirk: some NPOT DXT textures of the GUI display with corruption
522   // when using D3DPOOL_DEFAULT + D3DUSAGE_DYNAMIC textures (no other choice with D3D9Ex for example)
523   // most likely xbmc bug, but no hw to repro & fix properly.
524   // affects lots of hw generations - 6xxx, 7xxx, GT220, ION1
525   // see ticket #9269
526   if(m_defaultD3DUsage == D3DUSAGE_DYNAMIC
527   && m_defaultD3DPool  == D3DPOOL_DEFAULT
528   && m_AIdentifier.VendorId == PCIV_nVidia)
529   {
530     CLog::Log(LOGDEBUG, __FUNCTION__" - nVidia workaround - disabling RENDER_CAPS_DXT_NPOT");
531     m_renderCaps &= ~RENDER_CAPS_DXT_NPOT;
532   }
533
534   // Intel quirk: DXT texture pitch must be > 64
535   // when using D3DPOOL_DEFAULT + D3DUSAGE_DYNAMIC textures (no other choice with D3D9Ex)
536   // DXT1:   32 pixels wide is the largest non-working texture width
537   // DXT3/5: 16 pixels wide ----------------------------------------
538   // Both equal to a pitch of 64. So far no Intel has DXT NPOT (including i3/i5/i7, so just go with the next higher POT.
539   // See ticket #9578
540   if(m_defaultD3DUsage == D3DUSAGE_DYNAMIC
541   && m_defaultD3DPool  == D3DPOOL_DEFAULT
542   && m_AIdentifier.VendorId == PCIV_Intel)
543   {
544     CLog::Log(LOGDEBUG, __FUNCTION__" - Intel workaround - specifying minimum pitch for compressed textures.");
545     m_minDXTPitch = 128;
546   }
547
548   D3DDISPLAYMODE mode;
549   if (SUCCEEDED(m_pD3DDevice->GetDisplayMode(0, &mode)))
550     m_screenHeight = mode.Height;
551   else
552     m_screenHeight = m_nBackBufferHeight;
553
554   m_pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
555   m_pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
556
557   m_bRenderCreated = true;
558   m_needNewDevice = false;
559
560   // tell any shared objects about our resurrection
561   for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
562     (*i)->OnCreateDevice();
563
564   return true;
565 }
566
567 bool CRenderSystemDX::PresentRenderImpl(const CDirtyRegionList &dirty)
568 {
569   HRESULT hr;
570
571   if (!m_bRenderCreated)
572     return false;
573
574   if(m_nDeviceStatus != S_OK)
575     return false;
576
577   //CVideoReferenceClock polls GetRasterStatus too,
578   //polling it from two threads at the same time is bad
579   if (g_advancedSettings.m_sleepBeforeFlip > 0 && !g_VideoReferenceClock.IsRunning())
580   {
581     //save current thread priority and set thread priority to THREAD_PRIORITY_TIME_CRITICAL
582     int priority = GetThreadPriority(GetCurrentThread());
583     if (priority != THREAD_PRIORITY_ERROR_RETURN)
584       SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
585
586     D3DRASTER_STATUS rasterStatus;
587     int64_t          prev = CurrentHostCounter();
588
589     while (SUCCEEDED(m_pD3DDevice->GetRasterStatus(0, &rasterStatus)))
590     {
591       //wait for the scanline to go over the given proportion of m_screenHeight mark
592       if (!rasterStatus.InVBlank && rasterStatus.ScanLine >= g_advancedSettings.m_sleepBeforeFlip * m_screenHeight)
593         break;
594
595       //in theory it's possible this loop never exits, so don't let it run for longer than 100 ms
596       int64_t now = CurrentHostCounter();
597       if ((now - prev) * 10 > m_systemFreq)
598         break;
599
600       Sleep(1);
601     }
602
603     //restore thread priority
604     if (priority != THREAD_PRIORITY_ERROR_RETURN)
605       SetThreadPriority(GetCurrentThread(), priority);
606   }
607
608   hr = m_pD3DDevice->Present( NULL, NULL, 0, NULL );
609
610   if( D3DERR_DEVICELOST == hr )
611   {
612     CLog::Log(LOGDEBUG, "%s - lost device", __FUNCTION__);
613     return false;
614   }
615
616   if(FAILED(hr))
617   {
618     CLog::Log(LOGDEBUG, "%s - Present failed. %s", __FUNCTION__, GetErrorDescription(hr).c_str());
619     return false;
620   }
621
622   return true;
623 }
624
625 bool CRenderSystemDX::BeginRender()
626 {
627   if (!m_bRenderCreated)
628     return false;
629
630   HRESULT oldStatus = m_nDeviceStatus;
631   if (m_useD3D9Ex)
632   {
633     m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->CheckDeviceState(m_hDeviceWnd);
634
635     // handling of new D3D9 extensions return values. Others fallback to regular D3D9 handling.
636     switch(m_nDeviceStatus)
637     {
638     case S_PRESENT_MODE_CHANGED:
639       // Timing leads us here on occasion.
640       BuildPresentParameters();
641       m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->ResetEx(&m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX);
642       break;
643     case S_PRESENT_OCCLUDED:
644       m_nDeviceStatus = D3D_OK;
645       break;
646     case D3DERR_DEVICEHUNG:
647       CLog::Log(LOGERROR, "D3DERR_DEVICEHUNG");
648       m_nDeviceStatus = D3DERR_DEVICELOST;
649       m_needNewDevice = true;
650       break;
651     case D3DERR_OUTOFVIDEOMEMORY:
652       CLog::Log(LOGERROR, "D3DERR_OUTOFVIDEOMEMORY");
653       m_nDeviceStatus = D3DERR_DEVICELOST;
654       m_needNewDevice = true;
655       break;
656     case D3DERR_DEVICEREMOVED:
657       CLog::Log(LOGERROR, "D3DERR_DEVICEREMOVED");
658       m_nDeviceStatus = D3DERR_DEVICELOST;
659       m_needNewDevice = true;
660       // fixme: also needs to re-enumerate and switch to another screen
661       break;
662     }
663   }
664   else
665   {
666     m_nDeviceStatus = m_pD3DDevice->TestCooperativeLevel();
667   }
668
669   if( FAILED( m_nDeviceStatus ) )
670   {
671     // The device has been lost but cannot be reset at this time.
672     // Therefore, rendering is not possible and we'll have to return
673     // and try again at a later time.
674     if( m_nDeviceStatus == D3DERR_DEVICELOST )
675     {
676       if (m_nDeviceStatus != oldStatus)
677         CLog::Log(LOGDEBUG, "D3DERR_DEVICELOST");
678       OnDeviceLost();
679       return false;
680     }
681
682     // The device has been lost but it can be reset at this time.
683     if( m_nDeviceStatus == D3DERR_DEVICENOTRESET )
684     {
685       OnDeviceReset();
686       if( FAILED(m_nDeviceStatus ) )
687       {
688         CLog::Log(LOGINFO, "m_pD3DDevice->Reset failed");
689         return false;
690       }
691     }
692   }
693
694   HRESULT hr;
695
696   if(FAILED(hr = m_pD3DDevice->BeginScene()))
697   {
698     CLog::Log(LOGERROR, "m_pD3DDevice->BeginScene() failed. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
699     // When XBMC caught an exception after BeginScene(), EndScene() may never been called
700     // and thus all following BeginScene() will fail too.
701     if(FAILED(hr = m_pD3DDevice->EndScene()))
702       CLog::Log(LOGERROR, "m_pD3DDevice->EndScene() failed. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
703     return false;
704   }
705
706   IDirect3DSurface9 *pBackBuffer;
707   m_pD3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
708   m_pD3DDevice->SetRenderTarget(0, pBackBuffer);
709   pBackBuffer->Release();
710
711   m_inScene = true;
712   return true;
713 }
714
715 bool CRenderSystemDX::EndRender()
716 {
717   m_inScene = false;
718
719   if (!m_bRenderCreated)
720     return false;
721
722   if(m_nDeviceStatus != S_OK)
723     return false;
724
725   HRESULT hr = m_pD3DDevice->EndScene();
726   if(FAILED(hr))
727   {
728     CLog::Log(LOGERROR, "m_pD3DDevice->EndScene() failed. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
729     return false;
730   }
731
732   return true;
733 }
734
735 bool CRenderSystemDX::ClearBuffers(color_t color)
736 {
737   if (!m_bRenderCreated)
738     return false;
739
740   if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN
741   || m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
742   {
743     // if stereo anaglyph, data was cleared when left view was rendererd
744     if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
745       return true;
746   }
747
748   return SUCCEEDED(m_pD3DDevice->Clear(
749     0,
750     NULL,
751     D3DCLEAR_TARGET,
752     color,
753     1.0,
754     0 ) );
755
756   return true;
757 }
758
759 bool CRenderSystemDX::IsExtSupported(const char* extension)
760 {
761   return false;
762 }
763
764 bool CRenderSystemDX::PresentRender(const CDirtyRegionList &dirty)
765 {
766   if (!m_bRenderCreated)
767     return false;
768
769   bool result = PresentRenderImpl(dirty);
770
771   return result;
772 }
773
774 void CRenderSystemDX::SetVSync(bool enable)
775 {
776   if (m_bVSync != enable)
777   {
778     bool inScene(m_inScene);
779     if (m_inScene)
780       EndRender();
781     m_bVSync = enable;
782     ResetRenderSystem(m_nBackBufferWidth, m_nBackBufferHeight, m_bFullScreenDevice, m_refreshRate);
783     if (inScene)
784       BeginRender();
785   }
786 }
787
788 void CRenderSystemDX::CaptureStateBlock()
789 {
790   if (!m_bRenderCreated)
791     return;
792
793   SAFE_RELEASE(m_stateBlock);
794   m_pD3DDevice->CreateStateBlock(D3DSBT_ALL, &m_stateBlock);
795 }
796
797 void CRenderSystemDX::ApplyStateBlock()
798 {
799   if (!m_bRenderCreated)
800     return;
801
802   if (m_stateBlock)
803     m_stateBlock->Apply();
804 }
805
806 void CRenderSystemDX::SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight)
807 {
808   if (!m_bRenderCreated)
809     return;
810
811   // grab the viewport dimensions and location
812   D3DVIEWPORT9 viewport;
813   m_pD3DDevice->GetViewport(&viewport);
814   float w = viewport.Width*0.5f;
815   float h = viewport.Height*0.5f;
816
817   CPoint offset = camera - CPoint(screenWidth*0.5f, screenHeight*0.5f);
818
819   // world view.  Until this is moved onto the GPU (via a vertex shader for instance), we set it to the identity
820   // here.
821   D3DXMATRIX mtxWorld;
822   D3DXMatrixIdentity(&mtxWorld);
823   m_pD3DDevice->SetTransform(D3DTS_WORLD, &mtxWorld);
824
825   // camera view.  Multiply the Y coord by -1 then translate so that everything is relative to the camera
826   // position.
827   D3DXMATRIX flipY, translate, mtxView;
828   D3DXMatrixScaling(&flipY, 1.0f, -1.0f, 1.0f);
829   D3DXMatrixTranslation(&translate, -(w + offset.x), -(h + offset.y), 2*h);
830   D3DXMatrixMultiply(&mtxView, &translate, &flipY);
831   m_pD3DDevice->SetTransform(D3DTS_VIEW, &mtxView);
832
833   // projection onto screen space
834   D3DXMATRIX mtxProjection;
835   D3DXMatrixPerspectiveOffCenterLH(&mtxProjection, (-w - offset.x)*0.5f, (w - offset.x)*0.5f, (-h + offset.y)*0.5f, (h + offset.y)*0.5f, h, 100*h);
836   m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &mtxProjection);
837
838   m_world = mtxWorld;
839   m_view = mtxView;
840   m_projection = mtxProjection;
841   m_viewPort = viewport;
842 }
843
844 void CRenderSystemDX::Project(float &x, float &y, float &z)
845 {
846   D3DXVECTOR3 vScreenCoord;
847   D3DXVECTOR3 vLocation(x, y, z);
848
849   D3DXVec3Project(&vScreenCoord, &vLocation, &m_viewPort, &m_projection, &m_view, &m_world);
850   x = vScreenCoord.x;
851   y = vScreenCoord.y;
852   z = 0;
853 }
854
855 bool CRenderSystemDX::TestRender()
856 {
857   static unsigned int lastTime = 0;
858   static float delta = 0;
859
860   unsigned int thisTime = XbmcThreads::SystemClockMillis();
861
862   if(thisTime - lastTime > 10)
863   {
864     lastTime = thisTime;
865     delta++;
866   }
867
868   CLog::Log(LOGINFO, "Delta =  %d", delta);
869
870   if(delta > m_nBackBufferWidth)
871     delta = 0;
872
873   LPDIRECT3DVERTEXBUFFER9 pVB = NULL;
874
875   // A structure for our custom vertex type
876   struct CUSTOMVERTEX
877   {
878     FLOAT x, y, z, rhw; // The transformed position for the vertex
879     DWORD color;        // The vertex color
880   };
881
882   // Our custom FVF, which describes our custom vertex structure
883 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
884
885   // Initialize three vertices for rendering a triangle
886   CUSTOMVERTEX vertices[] =
887   {
888     { delta + 100.0f,  50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
889     { delta+200.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
890     {  delta, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
891   };
892
893   // Create the vertex buffer. Here we are allocating enough memory
894   // (from the default pool) to hold all our 3 custom vertices. We also
895   // specify the FVF, so the vertex buffer knows what data it contains.
896   if( FAILED( m_pD3DDevice->CreateVertexBuffer( 3 * sizeof( CUSTOMVERTEX ),
897     0, D3DFVF_CUSTOMVERTEX,
898     D3DPOOL_DEFAULT, &pVB, NULL ) ) )
899   {
900     return false;;
901   }
902
903   // Now we fill the vertex buffer. To do this, we need to Lock() the VB to
904   // gain access to the vertices. This mechanism is required becuase vertex
905   // buffers may be in device memory.
906   VOID* pVertices;
907   if( FAILED( pVB->Lock( 0, sizeof( vertices ), ( void** )&pVertices, 0 ) ) )
908     return false;
909   memcpy( pVertices, vertices, sizeof( vertices ) );
910   pVB->Unlock();
911
912   m_pD3DDevice->SetStreamSource( 0, pVB, 0, sizeof( CUSTOMVERTEX ) );
913   m_pD3DDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
914   m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
915
916   pVB->Release();
917
918   return true;
919 }
920
921 void CRenderSystemDX::ApplyHardwareTransform(const TransformMatrix &finalMatrix)
922 {
923   if (!m_bRenderCreated)
924     return;
925 }
926
927 void CRenderSystemDX::RestoreHardwareTransform()
928 {
929   if (!m_bRenderCreated)
930     return;
931 }
932
933 void CRenderSystemDX::GetViewPort(CRect& viewPort)
934 {
935   if (!m_bRenderCreated)
936     return;
937
938   D3DVIEWPORT9 d3dviewport;
939   m_pD3DDevice->GetViewport(&d3dviewport);
940
941   viewPort.x1 = (float)d3dviewport.X;
942   viewPort.y1 = (float)d3dviewport.Y;
943   viewPort.x2 = (float)d3dviewport.X + d3dviewport.Width;
944   viewPort.y2 = (float)d3dviewport.Y + d3dviewport.Height;
945 }
946
947 void CRenderSystemDX::SetViewPort(CRect& viewPort)
948 {
949   if (!m_bRenderCreated)
950     return;
951
952   D3DVIEWPORT9 newviewport;
953
954   newviewport.MinZ   = 0.0f;
955   newviewport.MaxZ   = 1.0f;
956   newviewport.X      = (DWORD)viewPort.x1;
957   newviewport.Y      = (DWORD)viewPort.y1;
958   newviewport.Width  = (DWORD)(viewPort.x2 - viewPort.x1);
959   newviewport.Height = (DWORD)(viewPort.y2 - viewPort.y1);
960   m_pD3DDevice->SetViewport(&newviewport);
961 }
962
963 void CRenderSystemDX::SetScissors(const CRect& rect)
964 {
965   if (!m_bRenderCreated)
966     return;
967
968   RECT scissor;
969   scissor.left   = MathUtils::round_int(rect.x1);
970   scissor.top    = MathUtils::round_int(rect.y1);
971   scissor.right  = MathUtils::round_int(rect.x2);
972   scissor.bottom = MathUtils::round_int(rect.y2);
973   m_pD3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
974   m_pD3DDevice->SetScissorRect(&scissor);
975 }
976
977 void CRenderSystemDX::ResetScissors()
978 {
979   if (!m_bRenderCreated)
980     return;
981
982   RECT scissor;
983   scissor.left = 0;
984   scissor.top = 0;
985   scissor.right = m_nBackBufferWidth;
986   scissor.bottom = m_nBackBufferHeight;
987   m_pD3DDevice->SetScissorRect(&scissor);
988   m_pD3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
989 }
990
991 void CRenderSystemDX::Register(ID3DResource *resource)
992 {
993   CSingleLock lock(m_resourceSection);
994   m_resources.push_back(resource);
995 }
996
997 void CRenderSystemDX::Unregister(ID3DResource* resource)
998 {
999   CSingleLock lock(m_resourceSection);
1000   vector<ID3DResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
1001   if (i != m_resources.end())
1002     m_resources.erase(i);
1003 }
1004
1005 CStdString CRenderSystemDX::GetErrorDescription(HRESULT hr)
1006 {
1007   return StringUtils::Format("%X - %s (%s)", hr, DXGetErrorString(hr), DXGetErrorDescription(hr));
1008 }
1009
1010 void CRenderSystemDX::SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view)
1011 {
1012   CRenderSystemBase::SetStereoMode(mode, view);
1013
1014   m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN);
1015   if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN)
1016   {
1017     if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
1018       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED );
1019     else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
1020       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN );
1021   }
1022   if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
1023   {
1024     if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
1025       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_GREEN );
1026     else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
1027       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_RED );
1028   }
1029 }
1030
1031 bool CRenderSystemDX::SupportsStereo(RENDER_STEREO_MODE mode) const
1032 {
1033   switch(mode)
1034   {
1035     case RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN:
1036     case RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA:
1037       return true;
1038     default:
1039       return CRenderSystemBase::SupportsStereo(mode);
1040   }
1041 }
1042
1043 void CRenderSystemDX::FlushGPU()
1044 {
1045   IDirect3DQuery9* pEvent = NULL;
1046
1047   m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEvent);
1048   if (pEvent != NULL)
1049   {
1050     pEvent->Issue(D3DISSUE_END);
1051     while (S_FALSE == pEvent->GetData(NULL, 0, D3DGETDATA_FLUSH))
1052       Sleep(1);
1053   }
1054 }
1055
1056 #endif