- Merge to trunk r37270.
[reactos.git] / reactos / dll / directx / d3d9 / adapter.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS ReactX
4 * FILE: dll/directx/d3d9/adapter.c
5 * PURPOSE: d3d9.dll adapter info functions
6 * PROGRAMERS: Gregor Brunmar <gregor (dot) brunmar (at) home (dot) se>
7 */
8
9 //#define _WIN32_WINNT 0x0502
10 #include "d3d9_common.h"
11 #include <d3d9.h>
12 #include <ddraw.h>
13 #include <debug.h>
14 #include <d3dhal.h>
15 #include "d3d9_private.h"
16 #include "d3d9_helpers.h"
17 #include "adapter.h"
18
19 #define D3D9_CAPS1 (D3DCAPS_READ_SCANLINE)
20 #define D3D9_PRE_XP_CAPS2 (D3DCAPS2_CANAUTOGENMIPMAP | D3DCAPS2_DYNAMICTEXTURES | D3DCAPS2_RESERVED | D3DCAPS2_FULLSCREENGAMMA)
21 #define D3D9_XP_OR_LATER_CAPS2 (D3D9_PRE_XP_CAPS2 | D3DCAPS2_CANMANAGERESOURCE)
22 #define D3D9_CAPS3 (D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD | D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION | D3DCAPS3_COPY_TO_VIDMEM | D3DCAPS3_COPY_TO_SYSTEMMEM)
23 #define D3D9_DEVCAPS (D3DDEVCAPS_EXECUTESYSTEMMEMORY | D3DDEVCAPS_EXECUTEVIDEOMEMORY | D3DDEVCAPS_TLVERTEXSYSTEMMEMORY \
24 | D3DDEVCAPS_TLVERTEXVIDEOMEMORY | D3DDEVCAPS_TEXTURESYSTEMMEMORY | D3DDEVCAPS_TEXTUREVIDEOMEMORY \
25 | D3DDEVCAPS_DRAWPRIMTLVERTEX | D3DDEVCAPS_CANRENDERAFTERFLIP | D3DDEVCAPS_TEXTURENONLOCALVIDMEM \
26 | D3DDEVCAPS_DRAWPRIMITIVES2 | D3DDEVCAPS_SEPARATETEXTUREMEMORIES | D3DDEVCAPS_DRAWPRIMITIVES2EX \
27 | D3DDEVCAPS_HWTRANSFORMANDLIGHT | D3DDEVCAPS_CANBLTSYSTONONLOCAL | D3DDEVCAPS_HWRASTERIZATION \
28 | D3DDEVCAPS_PUREDEVICE | D3DDEVCAPS_QUINTICRTPATCHES | D3DDEVCAPS_RTPATCHES | D3DDEVCAPS_RTPATCHHANDLEZERO \
29 | D3DDEVCAPS_NPATCHES)
30
31 #define D3DCAPS2_PRESENT_INTERVAL_SEVERAL 0x00200000
32 #define D3DCAPS2_PRESENT_INTERVAL_IMMEDIATE 0x00400000
33
34 #define D3DVTXPCAPS_FOGVERTEX 0x00000004
35
36 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
37 typedef BOOL (WINAPI *LPFN_DISABLEWOW64FSREDIRECTION) (PVOID*);
38 typedef BOOL (WINAPI *LPFN_REVERTWOW64FSREDIRECTION) (PVOID);
39
40
41 typedef struct _ADAPTERMONITOR
42 {
43 LPCSTR lpszDeviceName;
44 HMONITOR hMonitor;
45 } ADAPTERMONITOR, *LPADAPTERMONITOR;
46
47
48 static BOOL GetDriverName(LPDISPLAY_DEVICEA pDisplayDevice, D3DADAPTER_IDENTIFIER9* pIdentifier)
49 {
50 HKEY hKey;
51 BOOL bResult = FALSE;
52
53 if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_LOCAL_MACHINE, pDisplayDevice->DeviceKey + strlen("\\Registry\\Machine\\"), 0, KEY_QUERY_VALUE, &hKey))
54 {
55 DWORD DriverNameLength = MAX_DEVICE_IDENTIFIER_STRING - (DWORD)strlen(".dll");
56 DWORD Type = 0;
57
58 if (ERROR_SUCCESS == RegQueryValueExA(hKey, "InstalledDisplayDrivers", 0, &Type, (LPBYTE)pIdentifier->Driver, &DriverNameLength))
59 {
60 pIdentifier->Driver[DriverNameLength] = '\0';
61 SafeAppendString(pIdentifier->Driver, MAX_DEVICE_IDENTIFIER_STRING, ".dll");
62 bResult = TRUE;
63 }
64
65 RegCloseKey(hKey);
66 }
67
68 return bResult;
69 }
70
71 static void GetDriverVersion(LPDISPLAY_DEVICEA pDisplayDevice, D3DADAPTER_IDENTIFIER9* pIdentifier)
72 {
73 HMODULE hModule;
74 LPFN_ISWOW64PROCESS fnIsWow64Process;
75 BOOL bIsWow64 = FALSE;
76 PVOID OldWow64RedirectValue;
77 UINT DriverFileSize;
78
79 hModule = GetModuleHandleA("KERNEL32");
80 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(hModule, "IsWow64Process");
81 if (fnIsWow64Process)
82 {
83 fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
84 if (bIsWow64)
85 {
86 LPFN_DISABLEWOW64FSREDIRECTION fnDisableWow64FsRedirection;
87 fnDisableWow64FsRedirection = (LPFN_DISABLEWOW64FSREDIRECTION)GetProcAddress(hModule, "Wow64DisableWow64FsRedirection");
88 fnDisableWow64FsRedirection(&OldWow64RedirectValue);
89 }
90 }
91
92 DriverFileSize = GetFileVersionInfoSizeA(pIdentifier->Driver, NULL);
93 if (DriverFileSize > 0)
94 {
95 VS_FIXEDFILEINFO* FixedFileInfo = NULL;
96 LPVOID pBlock = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DriverFileSize);
97
98 if (TRUE == GetFileVersionInfoA(pIdentifier->Driver, 0, DriverFileSize, pBlock))
99 {
100 if (TRUE == VerQueryValueA(pBlock, "\\", (LPVOID*)&FixedFileInfo, &DriverFileSize))
101 {
102 pIdentifier->DriverVersion.HighPart = FixedFileInfo->dwFileVersionMS;
103 pIdentifier->DriverVersion.LowPart = FixedFileInfo->dwFileVersionLS;
104 }
105 }
106
107 HeapFree(GetProcessHeap(), 0, pBlock);
108 }
109
110 if (bIsWow64)
111 {
112 LPFN_REVERTWOW64FSREDIRECTION fnRevertWow64FsRedirection;
113 fnRevertWow64FsRedirection = (LPFN_REVERTWOW64FSREDIRECTION)GetProcAddress(hModule, "Wow64RevertWow64FsRedirection");
114 fnRevertWow64FsRedirection(&OldWow64RedirectValue);
115 }
116 }
117
118
119 static void ParseField(LPCSTR lpszDeviceKey, LPDWORD pField, LPCSTR lpszSubString)
120 {
121 const char* ResultStr;
122 ResultStr = strstr(lpszDeviceKey, lpszSubString);
123 if (ResultStr != NULL)
124 {
125 *pField = strtol(ResultStr + strlen(lpszSubString), NULL, 16);
126 }
127 }
128
129 static void GetDeviceId(LPCSTR lpszDeviceKey, D3DADAPTER_IDENTIFIER9* pIdentifier)
130 {
131 ParseField(lpszDeviceKey, &pIdentifier->VendorId, "VEN_");
132 ParseField(lpszDeviceKey, &pIdentifier->DeviceId, "DEV_");
133 ParseField(lpszDeviceKey, &pIdentifier->SubSysId, "SUBSYS_");
134 ParseField(lpszDeviceKey, &pIdentifier->Revision, "REV_");
135 }
136
137 static void GenerateDeviceIdentifier(D3DADAPTER_IDENTIFIER9* pIdentifier)
138 {
139 DWORD* dwIdentifier = (DWORD*)&pIdentifier->DeviceIdentifier;
140
141 pIdentifier->DeviceIdentifier = CLSID_DirectDraw;
142
143 dwIdentifier[0] ^= pIdentifier->VendorId;
144 dwIdentifier[1] ^= pIdentifier->DeviceId;
145 dwIdentifier[2] ^= pIdentifier->SubSysId;
146 dwIdentifier[3] ^= pIdentifier->Revision;
147 dwIdentifier[2] ^= pIdentifier->DriverVersion.LowPart;
148 dwIdentifier[3] ^= pIdentifier->DriverVersion.HighPart;
149 }
150
151 BOOL GetAdapterInfo(LPCSTR lpszDeviceName, D3DADAPTER_IDENTIFIER9* pIdentifier)
152 {
153 DISPLAY_DEVICEA DisplayDevice;
154 DWORD AdapterIndex;
155 BOOL FoundDisplayDevice;
156
157 memset(&DisplayDevice, 0, sizeof(DISPLAY_DEVICEA));
158 DisplayDevice.cb = sizeof(DISPLAY_DEVICEA);
159
160 AdapterIndex = 0;
161 FoundDisplayDevice = FALSE;
162 while (EnumDisplayDevicesA(NULL, AdapterIndex, &DisplayDevice, 0) == TRUE)
163 {
164 if (_stricmp(lpszDeviceName, DisplayDevice.DeviceName) == 0)
165 {
166 FoundDisplayDevice = TRUE;
167 break;
168 }
169
170 ++AdapterIndex;
171 }
172
173 /* No matching display device found? */
174 if (FALSE == FoundDisplayDevice)
175 return FALSE;
176
177 lstrcpynA(pIdentifier->Description, DisplayDevice.DeviceString, MAX_DEVICE_IDENTIFIER_STRING);
178 lstrcpynA(pIdentifier->DeviceName, DisplayDevice.DeviceName, CCHDEVICENAME);
179
180 if (GetDriverName(&DisplayDevice, pIdentifier) == TRUE)
181 GetDriverVersion(&DisplayDevice, pIdentifier);
182
183 GetDeviceId(DisplayDevice.DeviceID, pIdentifier);
184
185 GenerateDeviceIdentifier(pIdentifier);
186
187 return TRUE;
188 }
189
190
191
192 static BOOL IsWindowsXPorLaterCompatible()
193 {
194 OSVERSIONINFOA osvi;
195
196 ZeroMemory(&osvi, sizeof(OSVERSIONINFOA));
197 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
198
199 if (GetVersionExA(&osvi) != 0)
200 {
201 return ( (osvi.dwMajorVersion > 5) ||
202 ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) ));
203 }
204
205 return FALSE;
206 }
207
208 static void CopyDriverCaps(const D3DCAPS9* pSrcCaps, D3DCAPS9* pDstCaps)
209 {
210 *pDstCaps = *pSrcCaps;
211
212 pDstCaps->Caps = pSrcCaps->Caps & D3D9_CAPS1;
213
214 /* TODO: Fit in D3DCAPS2_CANCALIBRATEGAMMA somewhere here */
215 if (IsWindowsXPorLaterCompatible())
216 pDstCaps->Caps2 = pSrcCaps->Caps2 & D3D9_XP_OR_LATER_CAPS2;
217 else
218 pDstCaps->Caps2 = pSrcCaps->Caps2 & D3D9_PRE_XP_CAPS2;
219
220 pDstCaps->Caps3 = pSrcCaps->Caps3 & D3D9_CAPS3;
221 pDstCaps->DevCaps = pSrcCaps->DevCaps & D3D9_DEVCAPS;
222
223 pDstCaps->PresentationIntervals = D3DPRESENT_INTERVAL_ONE;
224 if (pSrcCaps->Caps2 & D3DCAPS2_PRESENT_INTERVAL_SEVERAL)
225 pDstCaps->PresentationIntervals |= (D3DPRESENT_INTERVAL_TWO | D3DPRESENT_INTERVAL_THREE | D3DPRESENT_INTERVAL_FOUR);
226 if (pSrcCaps->Caps2 & D3DCAPS2_PRESENT_INTERVAL_IMMEDIATE)
227 pDstCaps->PresentationIntervals |= D3DPRESENT_INTERVAL_IMMEDIATE;
228
229 pDstCaps->PrimitiveMiscCaps = pSrcCaps->PrimitiveMiscCaps & ~D3DPMISCCAPS_FOGINFVF;
230
231 if (pSrcCaps->VertexProcessingCaps & D3DVTXPCAPS_FOGVERTEX)
232 {
233 pDstCaps->RasterCaps |= D3DPRASTERCAPS_FOGVERTEX;
234 pDstCaps->VertexProcessingCaps &= ~D3DVTXPCAPS_FOGVERTEX;
235 }
236
237 if (pSrcCaps->MaxPointSize < 0.0f)
238 pDstCaps->MaxPointSize = 1.0f;
239 }
240
241 HRESULT GetAdapterCaps(const LPDIRECT3D9_DISPLAYADAPTER pDisplayAdapter, D3DDEVTYPE DeviceType, D3DCAPS9* pDstCaps)
242 {
243 HRESULT hResult = D3DERR_INVALIDDEVICE;
244 D3DCAPS9* pDriverCaps = NULL;
245
246 ZeroMemory(pDstCaps, sizeof(D3DCAPS9));
247
248 switch (DeviceType)
249 {
250 case D3DDEVTYPE_HAL:
251 pDriverCaps = &pDisplayAdapter->DriverCaps.DriverCaps9;
252 hResult = D3D_OK;
253 break;
254
255 case D3DDEVTYPE_REF:
256 case D3DDEVTYPE_SW:
257 case D3DDEVTYPE_NULLREF:
258 UNIMPLEMENTED;
259 hResult = D3D_OK;
260 break;
261
262 default:
263 DPRINT1("Unknown DeviceType argument");
264 break;
265 }
266
267 if (pDriverCaps != NULL)
268 {
269 CopyDriverCaps(pDriverCaps, pDstCaps);
270 }
271
272 if (SUCCEEDED(hResult))
273 {
274 pDstCaps->DeviceType = DeviceType;
275 pDstCaps->MasterAdapterOrdinal = pDisplayAdapter->MasterAdapterIndex;
276 pDstCaps->AdapterOrdinalInGroup = pDisplayAdapter->AdapterIndexInGroup;
277 pDstCaps->NumberOfAdaptersInGroup = pDisplayAdapter->NumAdaptersInGroup;
278 }
279
280 return hResult;
281 }
282
283
284
285 static D3DFORMAT Get16BitD3DFormat(LPCSTR lpszDeviceName)
286 {
287 HDC hDC;
288 HBITMAP hBitmap;
289 LPBITMAPINFO pBitmapInfo;
290 D3DFORMAT Format = D3DFMT_R5G6B5;
291
292 if (NULL == (hDC = CreateDCA(NULL, lpszDeviceName, NULL, NULL)))
293 {
294 return Format;
295 }
296
297 if (NULL == (hBitmap = CreateCompatibleBitmap(hDC, 1, 1)))
298 {
299 DeleteDC(hDC);
300 return Format;
301 }
302
303 pBitmapInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 4 * sizeof(RGBQUAD));
304 if (NULL == pBitmapInfo)
305 {
306 DeleteObject(hBitmap);
307 DeleteDC(hDC);
308 return Format;
309 }
310
311 pBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
312 if (GetDIBits(hDC, hBitmap, 0, 0, NULL, pBitmapInfo, DIB_RGB_COLORS) > 0)
313 {
314 if (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS)
315 {
316 if (GetDIBits(hDC, hBitmap, 0, pBitmapInfo->bmiHeader.biHeight, NULL, pBitmapInfo, DIB_RGB_COLORS) > 0)
317 {
318 /* Check if the green field is 6 bits long */
319 if (*(DWORD*)(&pBitmapInfo->bmiColors[1]) == 0x000003E0)
320 {
321 Format = D3DFMT_X1R5G5B5;
322 }
323 }
324 }
325 }
326
327 HeapFree(GetProcessHeap(), 0, pBitmapInfo);
328 DeleteObject(hBitmap);
329 DeleteDC(hDC);
330
331 return Format;
332 }
333
334 BOOL GetAdapterMode(LPCSTR lpszDeviceName, D3DDISPLAYMODE* pMode)
335 {
336 DEVMODEA DevMode;
337
338 memset(&DevMode, 0, sizeof(DEVMODEA));
339 DevMode.dmSize = sizeof(DEVMODEA);
340 if (FALSE == EnumDisplaySettingsA(lpszDeviceName, ENUM_CURRENT_SETTINGS, &DevMode))
341 return FALSE;
342
343 pMode->Width = DevMode.dmPelsWidth;
344 pMode->Height = DevMode.dmPelsHeight;
345 pMode->RefreshRate = DevMode.dmDisplayFrequency;
346
347 switch (DevMode.dmBitsPerPel)
348 {
349 case 8:
350 pMode->Format = D3DFMT_P8;
351 break;
352
353 case 16:
354 pMode->Format = Get16BitD3DFormat(lpszDeviceName);
355 break;
356
357 case 24:
358 pMode->Format = D3DFMT_R8G8B8;
359 break;
360
361 case 32:
362 pMode->Format = D3DFMT_X8R8G8B8;
363 break;
364
365 default:
366 pMode->Format = D3DFMT_UNKNOWN;
367 break;
368 }
369
370 return TRUE;
371 }
372
373
374
375 static BOOL CALLBACK AdapterMonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
376 {
377 MONITORINFOEXA MonitorInfoEx;
378 LPADAPTERMONITOR lpAdapterMonitor = (LPADAPTERMONITOR)dwData;
379
380 memset(&MonitorInfoEx, 0, sizeof(MONITORINFOEXA));
381 MonitorInfoEx.cbSize = sizeof(MONITORINFOEXA);
382
383 GetMonitorInfoA(hMonitor, (LPMONITORINFO)&MonitorInfoEx);
384
385 if (_stricmp(lpAdapterMonitor->lpszDeviceName, MonitorInfoEx.szDevice) == 0)
386 {
387 lpAdapterMonitor->hMonitor = hMonitor;
388 return FALSE;
389 }
390
391 return TRUE;
392 }
393
394 HMONITOR GetAdapterMonitor(LPCSTR lpszDeviceName)
395 {
396 ADAPTERMONITOR AdapterMonitor;
397 AdapterMonitor.lpszDeviceName = lpszDeviceName;
398 AdapterMonitor.hMonitor = NULL;
399
400 EnumDisplayMonitors(NULL, NULL, AdapterMonitorEnumProc, (LPARAM)&AdapterMonitor);
401
402 return AdapterMonitor.hMonitor;
403 }
404
405
406
407 UINT GetDisplayFormatCount(D3DFORMAT Format, const D3DDISPLAYMODE* pSupportedDisplayModes, UINT NumDisplayModes)
408 {
409 UINT DisplayModeIndex;
410 UINT FormatIndex = 0;
411
412 for (DisplayModeIndex = 0; DisplayModeIndex < NumDisplayModes; DisplayModeIndex++)
413 {
414 if (pSupportedDisplayModes[DisplayModeIndex].Format == Format)
415 {
416 ++FormatIndex;
417 }
418 }
419
420 return FormatIndex;
421 }
422
423 const D3DDISPLAYMODE* FindDisplayFormat(D3DFORMAT Format, UINT ModeIndex, const D3DDISPLAYMODE* pSupportedDisplayModes, UINT NumDisplayModes)
424 {
425 UINT DisplayModeIndex;
426 UINT FormatIndex = 0;
427
428 for (DisplayModeIndex = 0; DisplayModeIndex < NumDisplayModes; DisplayModeIndex++)
429 {
430 if (pSupportedDisplayModes[DisplayModeIndex].Format == Format)
431 {
432 if (ModeIndex == FormatIndex)
433 return &pSupportedDisplayModes[DisplayModeIndex];
434
435 ++FormatIndex;
436 }
437 }
438
439 if (FormatIndex == 0)
440 {
441 DPRINT1("No modes with the specified format found");
442 }
443 else if (FormatIndex < ModeIndex)
444 {
445 DPRINT1("Invalid mode index");
446 }
447
448 return NULL;
449 }