1 /*
2 * Copyright (c) 2016-2020 The Khronos Group Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * OpenCL is a trademark of Apple Inc. used under license by Khronos.
17 */
18
19 #include <initguid.h>
20
21 #include "icd.h"
22 #include "icd_windows.h"
23 #include "icd_windows_hkr.h"
24 #include "icd_windows_dxgk.h"
25 #include "icd_windows_apppackage.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <windows.h>
29 #include <winreg.h>
30
31 #include <dxgi.h>
32 typedef HRESULT (WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID, void **);
33
34 static INIT_ONCE initialized = INIT_ONCE_STATIC_INIT;
35
36 typedef struct WinAdapter
37 {
38 char * szName;
39 LUID luid;
40 } WinAdapter;
41
42 const LUID ZeroLuid = { 0, 0 };
43
44 static WinAdapter* pWinAdapterBegin = NULL;
45 static WinAdapter* pWinAdapterEnd = NULL;
46 static WinAdapter* pWinAdapterCapacity = NULL;
47
adapterAdd(const char * szName,LUID luid)48 BOOL adapterAdd(const char* szName, LUID luid)
49 {
50 BOOL result = TRUE;
51 if (pWinAdapterEnd == pWinAdapterCapacity)
52 {
53 size_t oldCapacity = pWinAdapterCapacity - pWinAdapterBegin;
54 size_t newCapacity = oldCapacity;
55 if (0 == newCapacity)
56 {
57 newCapacity = 1;
58 }
59 else if(newCapacity < UINT_MAX/2)
60 {
61 newCapacity *= 2;
62 }
63
64 WinAdapter* pNewBegin = malloc(newCapacity * sizeof(*pWinAdapterBegin));
65 if (!pNewBegin)
66 result = FALSE;
67 else
68 {
69 if (pWinAdapterBegin)
70 {
71 memcpy(pNewBegin, pWinAdapterBegin, oldCapacity * sizeof(*pWinAdapterBegin));
72 free(pWinAdapterBegin);
73 }
74 pWinAdapterCapacity = pNewBegin + newCapacity;
75 pWinAdapterEnd = pNewBegin + oldCapacity;
76 pWinAdapterBegin = pNewBegin;
77 }
78 }
79 if (pWinAdapterEnd != pWinAdapterCapacity)
80 {
81 size_t nameLen = (strlen(szName) + 1)*sizeof(szName[0]);
82 pWinAdapterEnd->szName = malloc(nameLen);
83 if (!pWinAdapterEnd->szName)
84 result = FALSE;
85 else
86 {
87 memcpy(pWinAdapterEnd->szName, szName, nameLen);
88 pWinAdapterEnd->luid = luid;
89 ++pWinAdapterEnd;
90 }
91 }
92 return result;
93 }
94
adapterFree(WinAdapter * pWinAdapter)95 void adapterFree(WinAdapter *pWinAdapter)
96 {
97 free(pWinAdapter->szName);
98 pWinAdapter->szName = NULL;
99 }
100
101 #if defined(CL_ENABLE_LAYERS)
102 typedef struct WinLayer
103 {
104 char * szName;
105 DWORD priority;
106 } WinLayer;
107
108 static WinLayer* pWinLayerBegin;
109 static WinLayer* pWinLayerEnd;
110 static WinLayer* pWinLayerCapacity;
111
compareLayer(const void * a,const void * b)112 static int __cdecl compareLayer(const void *a, const void *b)
113 {
114 return ((WinLayer *)a)->priority < ((WinLayer *)b)->priority ? -1 :
115 ((WinLayer *)a)->priority > ((WinLayer *)b)->priority ? 1 : 0;
116 }
117
layerAdd(const char * szName,DWORD priority)118 static BOOL layerAdd(const char* szName, DWORD priority)
119 {
120 BOOL result = TRUE;
121 if (pWinLayerEnd == pWinLayerCapacity)
122 {
123 size_t oldCapacity = pWinLayerCapacity - pWinLayerBegin;
124 size_t newCapacity = oldCapacity;
125 if (0 == newCapacity)
126 {
127 newCapacity = 1;
128 }
129 else if(newCapacity < UINT_MAX/2)
130 {
131 newCapacity *= 2;
132 }
133
134 WinLayer* pNewBegin = malloc(newCapacity * sizeof(*pWinLayerBegin));
135 if (!pNewBegin)
136 {
137 KHR_ICD_TRACE("Failed allocate space for Layers array\n");
138 result = FALSE;
139 }
140 else
141 {
142 if (pWinLayerBegin)
143 {
144 memcpy(pNewBegin, pWinLayerBegin, oldCapacity * sizeof(*pWinLayerBegin));
145 free(pWinLayerBegin);
146 }
147 pWinLayerCapacity = pNewBegin + newCapacity;
148 pWinLayerEnd = pNewBegin + oldCapacity;
149 pWinLayerBegin = pNewBegin;
150 }
151 }
152 if (pWinLayerEnd != pWinLayerCapacity)
153 {
154 size_t nameLen = (strlen(szName) + 1)*sizeof(szName[0]);
155 pWinLayerEnd->szName = malloc(nameLen);
156 if (!pWinLayerEnd->szName)
157 {
158 KHR_ICD_TRACE("Failed allocate space for Layer file path\n");
159 result = FALSE;
160 }
161 else
162 {
163 memcpy(pWinLayerEnd->szName, szName, nameLen);
164 pWinLayerEnd->priority = priority;
165 ++pWinLayerEnd;
166 }
167 }
168 return result;
169 }
170
layerFree(WinLayer * pWinLayer)171 void layerFree(WinLayer *pWinLayer)
172 {
173 free(pWinLayer->szName);
174 pWinLayer->szName = NULL;
175 }
176 #endif // defined(CL_ENABLE_LAYERS)
177
178 /*
179 *
180 * Vendor enumeration functions
181 *
182 */
183
184 // go through the list of vendors in the registry and call khrIcdVendorAdd
185 // for each vendor encountered
khrIcdOsVendorsEnumerate(PINIT_ONCE InitOnce,PVOID Parameter,PVOID * lpContext)186 BOOL CALLBACK khrIcdOsVendorsEnumerate(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
187 {
188 (void)InitOnce;
189 (void)Parameter;
190 (void)lpContext;
191
192 LONG result;
193 BOOL status = FALSE, currentStatus = FALSE;
194 const char* platformsName = "SOFTWARE\\Khronos\\OpenCL\\Vendors";
195 HKEY platformsKey = NULL;
196 DWORD dwIndex;
197
198 khrIcdInitializeTrace();
199 khrIcdVendorsEnumerateEnv();
200
201 currentStatus = khrIcdOsVendorsEnumerateDXGK();
202 status |= currentStatus;
203 if (!currentStatus)
204 {
205 KHR_ICD_TRACE("Failed to load via DXGK interface on RS4, continuing\n");
206 }
207
208 currentStatus = khrIcdOsVendorsEnumerateHKR();
209 status |= currentStatus;
210 if (!currentStatus)
211 {
212 KHR_ICD_TRACE("Failed to enumerate HKR entries, continuing\n");
213 }
214
215 currentStatus = khrIcdOsVendorsEnumerateAppPackage();
216 status |= currentStatus;
217 if (!currentStatus)
218 {
219 KHR_ICD_TRACE("Failed to enumerate App package entry, continuing\n");
220 }
221
222 KHR_ICD_TRACE("Opening key HKLM\\%s...\n", platformsName);
223 result = RegOpenKeyExA(
224 HKEY_LOCAL_MACHINE,
225 platformsName,
226 0,
227 KEY_READ,
228 &platformsKey);
229 if (ERROR_SUCCESS != result)
230 {
231 KHR_ICD_TRACE("Failed to open platforms key %s, continuing\n", platformsName);
232 }
233 else
234 {
235 // for each value
236 for (dwIndex = 0;; ++dwIndex)
237 {
238 char cszLibraryName[1024] = {0};
239 DWORD dwLibraryNameSize = sizeof(cszLibraryName);
240 DWORD dwLibraryNameType = 0;
241 DWORD dwValue = 0;
242 DWORD dwValueSize = sizeof(dwValue);
243
244 // read the value name
245 KHR_ICD_TRACE("Reading value %"PRIuDW"...\n", dwIndex);
246 result = RegEnumValueA(
247 platformsKey,
248 dwIndex,
249 cszLibraryName,
250 &dwLibraryNameSize,
251 NULL,
252 &dwLibraryNameType,
253 (LPBYTE)&dwValue,
254 &dwValueSize);
255 // if RegEnumKeyEx fails, we are done with the enumeration
256 if (ERROR_SUCCESS != result)
257 {
258 KHR_ICD_TRACE("Failed to read value %"PRIuDW", done reading key.\n", dwIndex);
259 break;
260 }
261 KHR_ICD_TRACE("Value %s found...\n", cszLibraryName);
262
263 // Require that the value be a DWORD and equal zero
264 if (REG_DWORD != dwLibraryNameType)
265 {
266 KHR_ICD_TRACE("Value not a DWORD, skipping\n");
267 continue;
268 }
269 if (dwValue)
270 {
271 KHR_ICD_TRACE("Value not zero, skipping\n");
272 continue;
273 }
274 // add the library
275 status |= adapterAdd(cszLibraryName, ZeroLuid);
276 }
277 }
278
279 // Add adapters according to DXGI's preference order
280 HMODULE hDXGI = LoadLibraryA("dxgi.dll");
281 if (hDXGI)
282 {
283 IDXGIFactory* pFactory = NULL;
284 PFN_CREATE_DXGI_FACTORY pCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY)GetProcAddress(hDXGI, "CreateDXGIFactory");
285 if (pCreateDXGIFactory)
286 {
287 HRESULT hr = pCreateDXGIFactory(&IID_IDXGIFactory, (void **)&pFactory);
288 if (SUCCEEDED(hr))
289 {
290 UINT i = 0;
291 IDXGIAdapter* pAdapter = NULL;
292 while (SUCCEEDED(pFactory->lpVtbl->EnumAdapters(pFactory, i++, &pAdapter)))
293 {
294 DXGI_ADAPTER_DESC AdapterDesc;
295 if (SUCCEEDED(pAdapter->lpVtbl->GetDesc(pAdapter, &AdapterDesc)))
296 {
297 for (WinAdapter* iterAdapter = pWinAdapterBegin; iterAdapter != pWinAdapterEnd; ++iterAdapter)
298 {
299 if (iterAdapter->luid.LowPart == AdapterDesc.AdapterLuid.LowPart
300 && iterAdapter->luid.HighPart == AdapterDesc.AdapterLuid.HighPart)
301 {
302 khrIcdVendorAdd(iterAdapter->szName);
303 break;
304 }
305 }
306 }
307
308 pAdapter->lpVtbl->Release(pAdapter);
309 }
310 pFactory->lpVtbl->Release(pFactory);
311 }
312 }
313 FreeLibrary(hDXGI);
314 }
315
316 // Go through the list again, putting any remaining adapters at the end of the list in an undefined order
317 for (WinAdapter* iterAdapter = pWinAdapterBegin; iterAdapter != pWinAdapterEnd; ++iterAdapter)
318 {
319 khrIcdVendorAdd(iterAdapter->szName);
320 adapterFree(iterAdapter);
321 }
322
323 free(pWinAdapterBegin);
324 pWinAdapterBegin = NULL;
325 pWinAdapterEnd = NULL;
326 pWinAdapterCapacity = NULL;
327
328 result = RegCloseKey(platformsKey);
329 if (ERROR_SUCCESS != result)
330 {
331 KHR_ICD_TRACE("Failed to close platforms key %s, ignoring\n", platformsName);
332 }
333
334 #if defined(CL_ENABLE_LAYERS)
335 const char* layersName = "SOFTWARE\\Khronos\\OpenCL\\Layers";
336 HKEY layersKey = NULL;
337
338 KHR_ICD_TRACE("Opening key HKLM\\%s...\n", layersName);
339 result = RegOpenKeyExA(
340 HKEY_LOCAL_MACHINE,
341 layersName,
342 0,
343 KEY_READ,
344 &layersKey);
345 if (ERROR_SUCCESS != result)
346 {
347 KHR_ICD_TRACE("Failed to open layers key %s, continuing\n", layersName);
348 }
349 else
350 {
351 // for each value
352 for (dwIndex = 0;; ++dwIndex)
353 {
354 char cszLibraryName[1024] = {0};
355 DWORD dwLibraryNameSize = sizeof(cszLibraryName);
356 DWORD dwLibraryNameType = 0;
357 DWORD dwValue = 0;
358 DWORD dwValueSize = sizeof(dwValue);
359
360 // read the value name
361 KHR_ICD_TRACE("Reading value %"PRIuDW"...\n", dwIndex);
362 result = RegEnumValueA(
363 layersKey,
364 dwIndex,
365 cszLibraryName,
366 &dwLibraryNameSize,
367 NULL,
368 &dwLibraryNameType,
369 (LPBYTE)&dwValue,
370 &dwValueSize);
371 // if RegEnumKeyEx fails, we are done with the enumeration
372 if (ERROR_SUCCESS != result)
373 {
374 KHR_ICD_TRACE("Failed to read value %"PRIuDW", done reading key.\n", dwIndex);
375 break;
376 }
377 KHR_ICD_TRACE("Value %s found...\n", cszLibraryName);
378
379 // Require that the value be a DWORD
380 if (REG_DWORD != dwLibraryNameType)
381 {
382 KHR_ICD_TRACE("Value not a DWORD, skipping\n");
383 continue;
384 }
385 // add the library
386 status |= layerAdd(cszLibraryName, dwValue);
387 }
388 qsort(pWinLayerBegin, pWinLayerEnd - pWinLayerBegin, sizeof(WinLayer), compareLayer);
389 for (WinLayer* iterLayer = pWinLayerBegin; iterLayer != pWinLayerEnd; ++iterLayer)
390 {
391 khrIcdLayerAdd(iterLayer->szName);
392 layerFree(iterLayer);
393 }
394 }
395
396 free(pWinLayerBegin);
397 pWinLayerBegin = NULL;
398 pWinLayerEnd = NULL;
399 pWinLayerCapacity = NULL;
400
401 result = RegCloseKey(layersKey);
402
403 khrIcdLayersEnumerateEnv();
404 #endif // defined(CL_ENABLE_LAYERS)
405 return status;
406 }
407
408 // go through the list of vendors only once
khrIcdOsVendorsEnumerateOnce()409 void khrIcdOsVendorsEnumerateOnce()
410 {
411 InitOnceExecuteOnce(&initialized, khrIcdOsVendorsEnumerate, NULL, NULL);
412 }
413
414 /*
415 *
416 * Dynamic library loading functions
417 *
418 */
419
420 // dynamically load a library. returns NULL on failure
khrIcdOsLibraryLoad(const char * libraryName)421 void *khrIcdOsLibraryLoad(const char *libraryName)
422 {
423 HMODULE hTemp = LoadLibraryExA(libraryName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
424 if (!hTemp && GetLastError() == ERROR_INVALID_PARAMETER)
425 {
426 hTemp = LoadLibraryExA(libraryName, NULL, 0);
427 }
428 if (!hTemp)
429 {
430 KHR_ICD_TRACE("Failed to load driver. Windows error code is %"PRIuDW".\n", GetLastError());
431 }
432 return (void*)hTemp;
433 }
434
435 // get a function pointer from a loaded library. returns NULL on failure.
khrIcdOsLibraryGetFunctionAddress(void * library,const char * functionName)436 void *khrIcdOsLibraryGetFunctionAddress(void *library, const char *functionName)
437 {
438 if (!library || !functionName)
439 {
440 return NULL;
441 }
442 return GetProcAddress( (HMODULE)library, functionName);
443 }
444
445 // unload a library.
khrIcdOsLibraryUnload(void * library)446 void khrIcdOsLibraryUnload(void *library)
447 {
448 FreeLibrary( (HMODULE)library);
449 }
450