2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
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"
37 #include "win32/WIN32Util.h"
38 #include "video/VideoReferenceClock.h"
39 #if (D3DX_SDK_VERSION >= 42) //aug 2009 sdk and up use dxerr
43 #define DXGetErrorString(hr) DXGetErrorString9(hr)
44 #define DXGetErrorDescription(hr) DXGetErrorDescription9(hr)
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;
54 static bool LoadD3D9Ex()
56 HMODULE hD3d9Dll = GetModuleHandle("d3d9.dll");
59 g_Direct3DCreate9Ex = (LPDIRECT3DCREATE9EX)GetProcAddress(hD3d9Dll, "Direct3DCreate9Ex" );
60 if(g_Direct3DCreate9Ex == NULL)
65 CRenderSystemDX::CRenderSystemDX() : CRenderSystemBase()
67 m_enumRenderingSystem = RENDERING_SYSTEM_DIRECTX;
71 m_devType = D3DDEVTYPE_HAL;
72 #if defined(DEBUG_PS) || defined (DEBUG_VS)
73 m_devType = D3DDEVTYPE_REF
77 m_nBackBufferWidth = 0;
78 m_nBackBufferHeight = 0;
79 m_bFullScreenDevice = false;
81 m_nDeviceStatus = S_OK;
84 m_needNewDevice = false;
85 m_adapter = D3DADAPTER_DEFAULT;
87 m_systemFreq = CurrentHostFrequency();
89 m_defaultD3DUsage = 0;
90 m_defaultD3DPool = D3DPOOL_MANAGED;
92 ZeroMemory(&m_D3DPP, sizeof(D3DPRESENT_PARAMETERS));
95 CRenderSystemDX::~CRenderSystemDX()
99 bool CRenderSystemDX::InitRenderSystem()
103 m_useD3D9Ex = (g_advancedSettings.m_AllowD3D9Ex && LoadD3D9Ex());
108 CLog::Log(LOGDEBUG, __FUNCTION__" - trying D3D9Ex...");
109 if (FAILED(g_Direct3DCreate9Ex(D3D_SDK_VERSION, (IDirect3D9Ex**) &m_pD3D)))
111 CLog::Log(LOGDEBUG, __FUNCTION__" - D3D9Ex creation failure, falling back to D3D9");
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)))
122 CLog::Log(LOGDEBUG, __FUNCTION__" - driver looks like XPDM or earlier, falling back to D3D9");
128 CLog::Log(LOGDEBUG, __FUNCTION__" - using D3D9Ex");
135 m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
142 if(CreateDevice()==false)
148 void CRenderSystemDX::SetRenderParams(unsigned int width, unsigned int height, bool fullScreen, float refreshRate)
150 m_nBackBufferWidth = width;
151 m_nBackBufferHeight = height;
152 m_bFullScreenDevice = fullScreen;
153 m_refreshRate = refreshRate;
156 void CRenderSystemDX::SetMonitor(HMONITOR monitor)
161 // find the appropriate screen
162 for (unsigned int adapter = 0; adapter < m_pD3D->GetAdapterCount(); adapter++)
164 HMONITOR hMonitor = m_pD3D->GetAdapterMonitor(adapter);
165 if (hMonitor == monitor && adapter != m_adapter)
168 m_needNewDevice = true;
174 bool CRenderSystemDX::ResetRenderSystem(int width, int height, bool fullScreen, float refreshRate)
179 if (m_hDeviceWnd != NULL)
181 HMONITOR hMonitor = MonitorFromWindow(m_hDeviceWnd, MONITOR_DEFAULTTONULL);
183 SetMonitor(hMonitor);
186 SetRenderParams(width, height, fullScreen, refreshRate);
189 rc.SetRect(0, 0, (float)width, (float)height);
192 BuildPresentParameters();
194 if (m_useD3D9Ex && !m_needNewDevice)
195 m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->ResetEx(&m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX);
205 void CRenderSystemDX::OnMove()
207 if (!m_bRenderCreated)
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);
217 bool CRenderSystemDX::IsSurfaceFormatOk(D3DFORMAT surfFormat, DWORD usage)
219 // Verify the compatibility
220 HRESULT hr = m_pD3D->CheckDeviceFormat(m_adapter,
222 m_D3DPP.BackBufferFormat,
227 return (SUCCEEDED(hr)) ? true : false;
230 bool CRenderSystemDX::IsTextureFormatOk(D3DFORMAT texFormat, DWORD usage)
232 // Verify the compatibility
233 HRESULT hr = m_pD3D->CheckDeviceFormat(m_adapter,
235 m_D3DPP.BackBufferFormat,
240 return (SUCCEEDED(hr)) ? true : false;
243 BOOL CRenderSystemDX::IsDepthFormatOk(D3DFORMAT DepthFormat, D3DFORMAT RenderTargetFormat)
245 // Verify that the depth format exists
246 if (!IsSurfaceFormatOk(DepthFormat, D3DUSAGE_DEPTHSTENCIL))
249 // Verify that the depth format is compatible
250 HRESULT hr = m_pD3D->CheckDepthStencilMatch(m_adapter,
252 m_D3DPP.BackBufferFormat,
256 return SUCCEEDED(hr);
259 void CRenderSystemDX::BuildPresentParameters()
261 OSVERSIONINFOEX osvi;
262 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
263 osvi.dwOSVersionInfoSize = sizeof(osvi);
264 GetVersionEx((OSVERSIONINFO *)&osvi);
266 ZeroMemory( &m_D3DPP, sizeof(D3DPRESENT_PARAMETERS) );
267 m_D3DPP.Windowed = m_useWindowedDX;
268 m_D3DPP.SwapEffect = D3DSWAPEFFECT_FLIP;
269 m_D3DPP.BackBufferCount = 2;
271 if(m_useD3D9Ex && (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 1 || osvi.dwMajorVersion > 6))
273 #if D3DX_SDK_VERSION >= 42
274 //m_D3DPP.SwapEffect = D3DSWAPEFFECT_FLIPEX;
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");
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;
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;
299 m_D3DPP.EnableAutoDepthStencil = TRUE;
300 m_D3DPP.AutoDepthStencilFormat = zFormat;
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;
314 bool CRenderSystemDX::DestroyRenderSystem()
318 SAFE_RELEASE(m_stateBlock);
319 SAFE_RELEASE(m_pD3D);
320 SAFE_RELEASE(m_pD3DDevice);
322 m_bRenderCreated = false;
327 void CRenderSystemDX::DeleteDevice()
329 CSingleLock lock(m_resourceSection);
331 // tell any shared resources
332 for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
333 (*i)->OnDestroyDevice();
335 SAFE_RELEASE(m_pD3DDevice);
336 m_bRenderCreated = false;
339 void CRenderSystemDX::OnDeviceLost()
341 CSingleLock lock(m_resourceSection);
342 g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_LOST);
343 SAFE_RELEASE(m_stateBlock);
349 // just resetting the device
350 for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
351 (*i)->OnLostDevice();
355 void CRenderSystemDX::OnDeviceReset()
357 CSingleLock lock(m_resourceSection);
365 m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->ResetEx(&m_D3DPP, m_D3DPP.Windowed ? NULL : &m_D3DDMEX);
367 m_nDeviceStatus = m_pD3DDevice->Reset(&m_D3DPP);
370 if (m_nDeviceStatus == S_OK)
372 for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
373 (*i)->OnResetDevice();
375 g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
379 for (vector<ID3DResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
380 (*i)->OnLostDevice();
384 bool CRenderSystemDX::CreateDevice()
386 // Code based on Ogre 3D engine
387 CSingleLock lock(m_resourceSection);
394 if(m_hDeviceWnd == NULL)
397 CLog::Log(LOGDEBUG, __FUNCTION__" on adapter %d", m_adapter);
399 BuildPresentParameters();
402 memset(&caps, 0, sizeof(caps));
403 m_pD3D->GetDeviceCaps(m_adapter, m_devType, &caps);
405 DWORD VertexProcessingFlags = 0;
406 if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
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;
413 VertexProcessingFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
414 CLog::Log(LOGDEBUG, __FUNCTION__" - using hardware vertex processing");
418 VertexProcessingFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
419 CLog::Log(LOGDEBUG, __FUNCTION__" - using software vertex processing");
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 );
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 );
435 CLog::Log(LOGERROR, __FUNCTION__" - unable to create a device. %s", GetErrorDescription(hr).c_str());
439 // Not sure the following actually does something
440 ((IDirect3DDevice9Ex*)m_pD3DDevice)->SetGPUThreadPriority(7);
444 hr = m_pD3D->CreateDevice(m_adapter, m_devType, m_hFocusWnd,
445 VertexProcessingFlags | D3DCREATE_MULTITHREADED, &m_D3DPP, &m_pD3DDevice );
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 );
455 CLog::Log(LOGERROR, __FUNCTION__" - unable to create a device. %s", GetErrorDescription(hr).c_str());
461 if(m_pD3D->GetAdapterIdentifier(m_adapter, 0, &m_AIdentifier) == D3D_OK)
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));
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);
472 // get our render capabilities
473 // re-read caps, there may be changes depending on the vertex processing type
474 m_pD3DDevice->GetDeviceCaps(&caps);
476 m_maxTextureSize = min(caps.MaxTextureWidth, caps.MaxTextureHeight);
478 if (g_advancedSettings.m_AllowDynamicTextures && m_useD3D9Ex && (caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES))
480 m_defaultD3DUsage = D3DUSAGE_DYNAMIC;
481 m_defaultD3DPool = D3DPOOL_DEFAULT;
482 CLog::Log(LOGDEBUG, __FUNCTION__" - using D3DCAPS2_DYNAMICTEXTURES");
486 m_defaultD3DUsage = 0;
487 m_defaultD3DPool = D3DPOOL_MANAGED;
492 CLog::Log(LOGDEBUG, __FUNCTION__" - texture caps: 0x%08X", caps.TextureCaps);
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;
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;
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;
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;
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");
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
526 if(m_defaultD3DUsage == D3DUSAGE_DYNAMIC
527 && m_defaultD3DPool == D3DPOOL_DEFAULT
528 && m_AIdentifier.VendorId == PCIV_nVidia)
530 CLog::Log(LOGDEBUG, __FUNCTION__" - nVidia workaround - disabling RENDER_CAPS_DXT_NPOT");
531 m_renderCaps &= ~RENDER_CAPS_DXT_NPOT;
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.
540 if(m_defaultD3DUsage == D3DUSAGE_DYNAMIC
541 && m_defaultD3DPool == D3DPOOL_DEFAULT
542 && m_AIdentifier.VendorId == PCIV_Intel)
544 CLog::Log(LOGDEBUG, __FUNCTION__" - Intel workaround - specifying minimum pitch for compressed textures.");
549 if (SUCCEEDED(m_pD3DDevice->GetDisplayMode(0, &mode)))
550 m_screenHeight = mode.Height;
552 m_screenHeight = m_nBackBufferHeight;
554 m_pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
555 m_pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
557 m_bRenderCreated = true;
558 m_needNewDevice = false;
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();
567 bool CRenderSystemDX::PresentRenderImpl(const CDirtyRegionList &dirty)
571 if (!m_bRenderCreated)
574 if(m_nDeviceStatus != S_OK)
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())
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);
586 D3DRASTER_STATUS rasterStatus;
587 int64_t prev = CurrentHostCounter();
589 while (SUCCEEDED(m_pD3DDevice->GetRasterStatus(0, &rasterStatus)))
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)
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)
603 //restore thread priority
604 if (priority != THREAD_PRIORITY_ERROR_RETURN)
605 SetThreadPriority(GetCurrentThread(), priority);
608 hr = m_pD3DDevice->Present( NULL, NULL, 0, NULL );
610 if( D3DERR_DEVICELOST == hr )
612 CLog::Log(LOGDEBUG, "%s - lost device", __FUNCTION__);
618 CLog::Log(LOGDEBUG, "%s - Present failed. %s", __FUNCTION__, GetErrorDescription(hr).c_str());
625 bool CRenderSystemDX::BeginRender()
627 if (!m_bRenderCreated)
630 HRESULT oldStatus = m_nDeviceStatus;
633 m_nDeviceStatus = ((IDirect3DDevice9Ex*)m_pD3DDevice)->CheckDeviceState(m_hDeviceWnd);
635 // handling of new D3D9 extensions return values. Others fallback to regular D3D9 handling.
636 switch(m_nDeviceStatus)
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);
643 case S_PRESENT_OCCLUDED:
644 m_nDeviceStatus = D3D_OK;
646 case D3DERR_DEVICEHUNG:
647 CLog::Log(LOGERROR, "D3DERR_DEVICEHUNG");
648 m_nDeviceStatus = D3DERR_DEVICELOST;
649 m_needNewDevice = true;
651 case D3DERR_OUTOFVIDEOMEMORY:
652 CLog::Log(LOGERROR, "D3DERR_OUTOFVIDEOMEMORY");
653 m_nDeviceStatus = D3DERR_DEVICELOST;
654 m_needNewDevice = true;
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
666 m_nDeviceStatus = m_pD3DDevice->TestCooperativeLevel();
669 if( FAILED( m_nDeviceStatus ) )
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 )
676 if (m_nDeviceStatus != oldStatus)
677 CLog::Log(LOGDEBUG, "D3DERR_DEVICELOST");
682 // The device has been lost but it can be reset at this time.
683 if( m_nDeviceStatus == D3DERR_DEVICENOTRESET )
686 if( FAILED(m_nDeviceStatus ) )
688 CLog::Log(LOGINFO, "m_pD3DDevice->Reset failed");
696 if(FAILED(hr = m_pD3DDevice->BeginScene()))
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());
706 IDirect3DSurface9 *pBackBuffer;
707 m_pD3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
708 m_pD3DDevice->SetRenderTarget(0, pBackBuffer);
709 pBackBuffer->Release();
715 bool CRenderSystemDX::EndRender()
719 if (!m_bRenderCreated)
722 if(m_nDeviceStatus != S_OK)
725 HRESULT hr = m_pD3DDevice->EndScene();
728 CLog::Log(LOGERROR, "m_pD3DDevice->EndScene() failed. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
735 bool CRenderSystemDX::ClearBuffers(color_t color)
737 if (!m_bRenderCreated)
740 if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN
741 || m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
743 // if stereo anaglyph, data was cleared when left view was rendererd
744 if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
748 return SUCCEEDED(m_pD3DDevice->Clear(
759 bool CRenderSystemDX::IsExtSupported(const char* extension)
764 bool CRenderSystemDX::PresentRender(const CDirtyRegionList &dirty)
766 if (!m_bRenderCreated)
769 bool result = PresentRenderImpl(dirty);
774 void CRenderSystemDX::SetVSync(bool enable)
776 if (m_bVSync != enable)
778 bool inScene(m_inScene);
782 ResetRenderSystem(m_nBackBufferWidth, m_nBackBufferHeight, m_bFullScreenDevice, m_refreshRate);
788 void CRenderSystemDX::CaptureStateBlock()
790 if (!m_bRenderCreated)
793 SAFE_RELEASE(m_stateBlock);
794 m_pD3DDevice->CreateStateBlock(D3DSBT_ALL, &m_stateBlock);
797 void CRenderSystemDX::ApplyStateBlock()
799 if (!m_bRenderCreated)
803 m_stateBlock->Apply();
806 void CRenderSystemDX::SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight)
808 if (!m_bRenderCreated)
811 // grab the viewport dimensions and location
812 float w = m_viewPort.Width*0.5f;
813 float h = m_viewPort.Height*0.5f;
815 CPoint offset = camera - CPoint(screenWidth*0.5f, screenHeight*0.5f);
817 // world view. Until this is moved onto the GPU (via a vertex shader for instance), we set it to the identity
820 D3DXMatrixIdentity(&mtxWorld);
821 m_pD3DDevice->SetTransform(D3DTS_WORLD, &mtxWorld);
823 // camera view. Multiply the Y coord by -1 then translate so that everything is relative to the camera
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);
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);
838 m_projection = mtxProjection;
841 void CRenderSystemDX::Project(float &x, float &y, float &z)
843 D3DXVECTOR3 vScreenCoord;
844 D3DXVECTOR3 vLocation(x, y, z);
846 D3DXVec3Project(&vScreenCoord, &vLocation, &m_viewPort, &m_projection, &m_view, &m_world);
852 bool CRenderSystemDX::TestRender()
854 static unsigned int lastTime = 0;
855 static float delta = 0;
857 unsigned int thisTime = XbmcThreads::SystemClockMillis();
859 if(thisTime - lastTime > 10)
865 CLog::Log(LOGINFO, "Delta = %d", delta);
867 if(delta > m_nBackBufferWidth)
870 LPDIRECT3DVERTEXBUFFER9 pVB = NULL;
872 // A structure for our custom vertex type
875 FLOAT x, y, z, rhw; // The transformed position for the vertex
876 DWORD color; // The vertex color
879 // Our custom FVF, which describes our custom vertex structure
880 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
882 // Initialize three vertices for rendering a triangle
883 CUSTOMVERTEX vertices[] =
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, },
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 ) ) )
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.
904 if( FAILED( pVB->Lock( 0, sizeof( vertices ), ( void** )&pVertices, 0 ) ) )
906 memcpy( pVertices, vertices, sizeof( vertices ) );
909 m_pD3DDevice->SetStreamSource( 0, pVB, 0, sizeof( CUSTOMVERTEX ) );
910 m_pD3DDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
911 m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
918 void CRenderSystemDX::ApplyHardwareTransform(const TransformMatrix &finalMatrix)
920 if (!m_bRenderCreated)
924 void CRenderSystemDX::RestoreHardwareTransform()
926 if (!m_bRenderCreated)
930 void CRenderSystemDX::GetViewPort(CRect& viewPort)
932 if (!m_bRenderCreated)
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;
941 void CRenderSystemDX::SetViewPort(CRect& viewPort)
943 if (!m_bRenderCreated)
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);
955 void CRenderSystemDX::SetScissors(const CRect& rect)
957 if (!m_bRenderCreated)
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);
969 void CRenderSystemDX::ResetScissors()
971 if (!m_bRenderCreated)
977 scissor.right = m_nBackBufferWidth;
978 scissor.bottom = m_nBackBufferHeight;
979 m_pD3DDevice->SetScissorRect(&scissor);
980 m_pD3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
983 void CRenderSystemDX::Register(ID3DResource *resource)
985 CSingleLock lock(m_resourceSection);
986 m_resources.push_back(resource);
989 void CRenderSystemDX::Unregister(ID3DResource* resource)
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);
997 CStdString CRenderSystemDX::GetErrorDescription(HRESULT hr)
999 return StringUtils::Format("%X - %s (%s)", hr, DXGetErrorString(hr), DXGetErrorDescription(hr));
1002 void CRenderSystemDX::SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view)
1004 CRenderSystemBase::SetStereoMode(mode, view);
1006 m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN);
1007 if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN)
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 );
1014 if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
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 );
1023 bool CRenderSystemDX::SupportsStereo(RENDER_STEREO_MODE mode) const
1027 case RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN:
1028 case RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA:
1031 return CRenderSystemBase::SupportsStereo(mode);
1035 void CRenderSystemDX::FlushGPU()
1037 IDirect3DQuery9* pEvent = NULL;
1039 m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEvent);
1042 pEvent->Issue(D3DISSUE_END);
1043 while (S_FALSE == pEvent->GetData(NULL, 0, D3DGETDATA_FLUSH))