[WIN32] avoid getting the viewport so often.
[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   float w = m_viewPort.Width*0.5f;
813   float h = m_viewPort.Height*0.5f;
814
815   CPoint offset = camera - CPoint(screenWidth*0.5f, screenHeight*0.5f);
816
817   // world view.  Until this is moved onto the GPU (via a vertex shader for instance), we set it to the identity
818   // here.
819   D3DXMATRIX mtxWorld;
820   D3DXMatrixIdentity(&mtxWorld);
821   m_pD3DDevice->SetTransform(D3DTS_WORLD, &mtxWorld);
822
823   // camera view.  Multiply the Y coord by -1 then translate so that everything is relative to the camera
824   // position.
825   D3DXMATRIX flipY, translate, mtxView;
826   D3DXMatrixScaling(&flipY, 1.0f, -1.0f, 1.0f);
827   D3DXMatrixTranslation(&translate, -(w + offset.x), -(h + offset.y), 2*h);
828   D3DXMatrixMultiply(&mtxView, &translate, &flipY);
829   m_pD3DDevice->SetTransform(D3DTS_VIEW, &mtxView);
830
831   // projection onto screen space
832   D3DXMATRIX mtxProjection;
833   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);
834   m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &mtxProjection);
835
836   m_world = mtxWorld;
837   m_view = mtxView;
838   m_projection = mtxProjection;
839 }
840
841 void CRenderSystemDX::Project(float &x, float &y, float &z)
842 {
843   D3DXVECTOR3 vScreenCoord;
844   D3DXVECTOR3 vLocation(x, y, z);
845
846   D3DXVec3Project(&vScreenCoord, &vLocation, &m_viewPort, &m_projection, &m_view, &m_world);
847   x = vScreenCoord.x;
848   y = vScreenCoord.y;
849   z = 0;
850 }
851
852 bool CRenderSystemDX::TestRender()
853 {
854   static unsigned int lastTime = 0;
855   static float delta = 0;
856
857   unsigned int thisTime = XbmcThreads::SystemClockMillis();
858
859   if(thisTime - lastTime > 10)
860   {
861     lastTime = thisTime;
862     delta++;
863   }
864
865   CLog::Log(LOGINFO, "Delta =  %d", delta);
866
867   if(delta > m_nBackBufferWidth)
868     delta = 0;
869
870   LPDIRECT3DVERTEXBUFFER9 pVB = NULL;
871
872   // A structure for our custom vertex type
873   struct CUSTOMVERTEX
874   {
875     FLOAT x, y, z, rhw; // The transformed position for the vertex
876     DWORD color;        // The vertex color
877   };
878
879   // Our custom FVF, which describes our custom vertex structure
880 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
881
882   // Initialize three vertices for rendering a triangle
883   CUSTOMVERTEX vertices[] =
884   {
885     { delta + 100.0f,  50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
886     { delta+200.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
887     {  delta, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
888   };
889
890   // Create the vertex buffer. Here we are allocating enough memory
891   // (from the default pool) to hold all our 3 custom vertices. We also
892   // specify the FVF, so the vertex buffer knows what data it contains.
893   if( FAILED( m_pD3DDevice->CreateVertexBuffer( 3 * sizeof( CUSTOMVERTEX ),
894     0, D3DFVF_CUSTOMVERTEX,
895     D3DPOOL_DEFAULT, &pVB, NULL ) ) )
896   {
897     return false;;
898   }
899
900   // Now we fill the vertex buffer. To do this, we need to Lock() the VB to
901   // gain access to the vertices. This mechanism is required becuase vertex
902   // buffers may be in device memory.
903   VOID* pVertices;
904   if( FAILED( pVB->Lock( 0, sizeof( vertices ), ( void** )&pVertices, 0 ) ) )
905     return false;
906   memcpy( pVertices, vertices, sizeof( vertices ) );
907   pVB->Unlock();
908
909   m_pD3DDevice->SetStreamSource( 0, pVB, 0, sizeof( CUSTOMVERTEX ) );
910   m_pD3DDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
911   m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
912
913   pVB->Release();
914
915   return true;
916 }
917
918 void CRenderSystemDX::ApplyHardwareTransform(const TransformMatrix &finalMatrix)
919 {
920   if (!m_bRenderCreated)
921     return;
922 }
923
924 void CRenderSystemDX::RestoreHardwareTransform()
925 {
926   if (!m_bRenderCreated)
927     return;
928 }
929
930 void CRenderSystemDX::GetViewPort(CRect& viewPort)
931 {
932   if (!m_bRenderCreated)
933     return;
934
935   viewPort.x1 = (float)m_viewPort.X;
936   viewPort.y1 = (float)m_viewPort.Y;
937   viewPort.x2 = (float)m_viewPort.X + m_viewPort.Width;
938   viewPort.y2 = (float)m_viewPort.Y + m_viewPort.Height;
939 }
940
941 void CRenderSystemDX::SetViewPort(CRect& viewPort)
942 {
943   if (!m_bRenderCreated)
944     return;
945
946   m_viewPort.MinZ   = 0.0f;
947   m_viewPort.MaxZ   = 1.0f;
948   m_viewPort.X      = (DWORD)viewPort.x1;
949   m_viewPort.Y      = (DWORD)viewPort.y1;
950   m_viewPort.Width  = (DWORD)(viewPort.x2 - viewPort.x1);
951   m_viewPort.Height = (DWORD)(viewPort.y2 - viewPort.y1);
952   m_pD3DDevice->SetViewport(&m_viewPort);
953 }
954
955 void CRenderSystemDX::SetScissors(const CRect& rect)
956 {
957   if (!m_bRenderCreated)
958     return;
959
960   RECT scissor;
961   scissor.left   = MathUtils::round_int(rect.x1);
962   scissor.top    = MathUtils::round_int(rect.y1);
963   scissor.right  = MathUtils::round_int(rect.x2);
964   scissor.bottom = MathUtils::round_int(rect.y2);
965   m_pD3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
966   m_pD3DDevice->SetScissorRect(&scissor);
967 }
968
969 void CRenderSystemDX::ResetScissors()
970 {
971   if (!m_bRenderCreated)
972     return;
973
974   RECT scissor;
975   scissor.left = 0;
976   scissor.top = 0;
977   scissor.right = m_nBackBufferWidth;
978   scissor.bottom = m_nBackBufferHeight;
979   m_pD3DDevice->SetScissorRect(&scissor);
980   m_pD3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
981 }
982
983 void CRenderSystemDX::Register(ID3DResource *resource)
984 {
985   CSingleLock lock(m_resourceSection);
986   m_resources.push_back(resource);
987 }
988
989 void CRenderSystemDX::Unregister(ID3DResource* resource)
990 {
991   CSingleLock lock(m_resourceSection);
992   vector<ID3DResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
993   if (i != m_resources.end())
994     m_resources.erase(i);
995 }
996
997 CStdString CRenderSystemDX::GetErrorDescription(HRESULT hr)
998 {
999   return StringUtils::Format("%X - %s (%s)", hr, DXGetErrorString(hr), DXGetErrorDescription(hr));
1000 }
1001
1002 void CRenderSystemDX::SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view)
1003 {
1004   CRenderSystemBase::SetStereoMode(mode, view);
1005
1006   m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN);
1007   if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN)
1008   {
1009     if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
1010       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED );
1011     else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
1012       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN );
1013   }
1014   if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
1015   {
1016     if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
1017       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_GREEN );
1018     else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
1019       m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_RED );
1020   }
1021 }
1022
1023 bool CRenderSystemDX::SupportsStereo(RENDER_STEREO_MODE mode) const
1024 {
1025   switch(mode)
1026   {
1027     case RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN:
1028     case RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA:
1029       return true;
1030     default:
1031       return CRenderSystemBase::SupportsStereo(mode);
1032   }
1033 }
1034
1035 void CRenderSystemDX::FlushGPU()
1036 {
1037   IDirect3DQuery9* pEvent = NULL;
1038
1039   m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEvent);
1040   if (pEvent != NULL)
1041   {
1042     pEvent->Issue(D3DISSUE_END);
1043     while (S_FALSE == pEvent->GetData(NULL, 0, D3DGETDATA_FLUSH))
1044       Sleep(1);
1045   }
1046 }
1047
1048 #endif