xref: /aosp_15_r20/external/OpenCL-ICD-Loader/loader/windows/icd_windows.c (revision 1cddb830dba8aa7c1cc1039338e56b3b9fa24952)
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 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     LONG result;
189     BOOL status = FALSE, currentStatus = FALSE;
190     const char* platformsName = "SOFTWARE\\Khronos\\OpenCL\\Vendors";
191     HKEY platformsKey = NULL;
192     DWORD dwIndex;
193 
194     khrIcdInitializeTrace();
195     khrIcdVendorsEnumerateEnv();
196 
197     currentStatus = khrIcdOsVendorsEnumerateDXGK();
198     status |= currentStatus;
199     if (!currentStatus)
200     {
201         KHR_ICD_TRACE("Failed to load via DXGK interface on RS4, continuing\n");
202     }
203 
204     currentStatus = khrIcdOsVendorsEnumerateHKR();
205     status |= currentStatus;
206     if (!currentStatus)
207     {
208         KHR_ICD_TRACE("Failed to enumerate HKR entries, continuing\n");
209     }
210 
211     currentStatus = khrIcdOsVendorsEnumerateAppPackage();
212     status |= currentStatus;
213     if (!currentStatus)
214     {
215         KHR_ICD_TRACE("Failed to enumerate App package entry, continuing\n");
216     }
217 
218     KHR_ICD_TRACE("Opening key HKLM\\%s...\n", platformsName);
219     result = RegOpenKeyExA(
220         HKEY_LOCAL_MACHINE,
221         platformsName,
222         0,
223         KEY_READ,
224         &platformsKey);
225     if (ERROR_SUCCESS != result)
226     {
227         KHR_ICD_TRACE("Failed to open platforms key %s, continuing\n", platformsName);
228     }
229     else
230     {
231         // for each value
232         for (dwIndex = 0;; ++dwIndex)
233         {
234             char cszLibraryName[1024] = {0};
235             DWORD dwLibraryNameSize = sizeof(cszLibraryName);
236             DWORD dwLibraryNameType = 0;
237             DWORD dwValue = 0;
238             DWORD dwValueSize = sizeof(dwValue);
239 
240             // read the value name
241             KHR_ICD_TRACE("Reading value %"PRIuDW"...\n", dwIndex);
242             result = RegEnumValueA(
243                   platformsKey,
244                   dwIndex,
245                   cszLibraryName,
246                   &dwLibraryNameSize,
247                   NULL,
248                   &dwLibraryNameType,
249                   (LPBYTE)&dwValue,
250                   &dwValueSize);
251             // if RegEnumKeyEx fails, we are done with the enumeration
252             if (ERROR_SUCCESS != result)
253             {
254                 KHR_ICD_TRACE("Failed to read value %"PRIuDW", done reading key.\n", dwIndex);
255                 break;
256             }
257             KHR_ICD_TRACE("Value %s found...\n", cszLibraryName);
258 
259             // Require that the value be a DWORD and equal zero
260             if (REG_DWORD != dwLibraryNameType)
261             {
262                 KHR_ICD_TRACE("Value not a DWORD, skipping\n");
263                 continue;
264             }
265             if (dwValue)
266             {
267                 KHR_ICD_TRACE("Value not zero, skipping\n");
268                 continue;
269             }
270             // add the library
271             status |= adapterAdd(cszLibraryName, ZeroLuid);
272         }
273     }
274 
275     // Add adapters according to DXGI's preference order
276     HMODULE hDXGI = LoadLibraryA("dxgi.dll");
277     if (hDXGI)
278     {
279         IDXGIFactory* pFactory = NULL;
280         PFN_CREATE_DXGI_FACTORY pCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY)GetProcAddress(hDXGI, "CreateDXGIFactory");
281         if (pCreateDXGIFactory)
282         {
283             HRESULT hr = pCreateDXGIFactory(&IID_IDXGIFactory, (void **)&pFactory);
284             if (SUCCEEDED(hr))
285             {
286                 UINT i = 0;
287                 IDXGIAdapter* pAdapter = NULL;
288                 while (SUCCEEDED(pFactory->lpVtbl->EnumAdapters(pFactory, i++, &pAdapter)))
289                 {
290                     DXGI_ADAPTER_DESC AdapterDesc;
291                     if (SUCCEEDED(pAdapter->lpVtbl->GetDesc(pAdapter, &AdapterDesc)))
292                     {
293                         for (WinAdapter* iterAdapter = pWinAdapterBegin; iterAdapter != pWinAdapterEnd; ++iterAdapter)
294                         {
295                             if (iterAdapter->luid.LowPart == AdapterDesc.AdapterLuid.LowPart
296                                 && iterAdapter->luid.HighPart == AdapterDesc.AdapterLuid.HighPart)
297                             {
298                                 khrIcdVendorAdd(iterAdapter->szName);
299                                 break;
300                             }
301                         }
302                     }
303 
304                     pAdapter->lpVtbl->Release(pAdapter);
305                 }
306                 pFactory->lpVtbl->Release(pFactory);
307             }
308         }
309         FreeLibrary(hDXGI);
310     }
311 
312     // Go through the list again, putting any remaining adapters at the end of the list in an undefined order
313     for (WinAdapter* iterAdapter = pWinAdapterBegin; iterAdapter != pWinAdapterEnd; ++iterAdapter)
314     {
315         khrIcdVendorAdd(iterAdapter->szName);
316         adapterFree(iterAdapter);
317     }
318 
319     free(pWinAdapterBegin);
320     pWinAdapterBegin = NULL;
321     pWinAdapterEnd = NULL;
322     pWinAdapterCapacity = NULL;
323 
324     result = RegCloseKey(platformsKey);
325     if (ERROR_SUCCESS != result)
326     {
327         KHR_ICD_TRACE("Failed to close platforms key %s, ignoring\n", platformsName);
328     }
329 
330 #if defined(CL_ENABLE_LAYERS)
331     const char* layersName = "SOFTWARE\\Khronos\\OpenCL\\Layers";
332     HKEY layersKey = NULL;
333 
334     KHR_ICD_TRACE("Opening key HKLM\\%s...\n", layersName);
335     result = RegOpenKeyExA(
336         HKEY_LOCAL_MACHINE,
337         layersName,
338         0,
339         KEY_READ,
340         &layersKey);
341     if (ERROR_SUCCESS != result)
342     {
343         KHR_ICD_TRACE("Failed to open layers key %s, continuing\n", layersName);
344     }
345     else
346     {
347         // for each value
348         for (dwIndex = 0;; ++dwIndex)
349         {
350             char cszLibraryName[1024] = {0};
351             DWORD dwLibraryNameSize = sizeof(cszLibraryName);
352             DWORD dwLibraryNameType = 0;
353             DWORD dwValue = 0;
354             DWORD dwValueSize = sizeof(dwValue);
355 
356             // read the value name
357             KHR_ICD_TRACE("Reading value %"PRIuDW"...\n", dwIndex);
358             result = RegEnumValueA(
359                   layersKey,
360                   dwIndex,
361                   cszLibraryName,
362                   &dwLibraryNameSize,
363                   NULL,
364                   &dwLibraryNameType,
365                   (LPBYTE)&dwValue,
366                   &dwValueSize);
367             // if RegEnumKeyEx fails, we are done with the enumeration
368             if (ERROR_SUCCESS != result)
369             {
370                 KHR_ICD_TRACE("Failed to read value %"PRIuDW", done reading key.\n", dwIndex);
371                 break;
372             }
373             KHR_ICD_TRACE("Value %s found...\n", cszLibraryName);
374 
375             // Require that the value be a DWORD
376             if (REG_DWORD != dwLibraryNameType)
377             {
378                 KHR_ICD_TRACE("Value not a DWORD, skipping\n");
379                 continue;
380             }
381             // add the library
382             status |= layerAdd(cszLibraryName, dwValue);
383         }
384         qsort(pWinLayerBegin, pWinLayerEnd - pWinLayerBegin, sizeof(WinLayer), compareLayer);
385         for (WinLayer* iterLayer = pWinLayerBegin; iterLayer != pWinLayerEnd; ++iterLayer)
386         {
387             khrIcdLayerAdd(iterLayer->szName);
388             layerFree(iterLayer);
389         }
390     }
391 
392     free(pWinLayerBegin);
393     pWinLayerBegin = NULL;
394     pWinLayerEnd = NULL;
395     pWinLayerCapacity = NULL;
396 
397     result = RegCloseKey(layersKey);
398 
399     khrIcdLayersEnumerateEnv();
400 #endif // defined(CL_ENABLE_LAYERS)
401     return status;
402 }
403 
404 // go through the list of vendors only once
khrIcdOsVendorsEnumerateOnce()405 void khrIcdOsVendorsEnumerateOnce()
406 {
407     InitOnceExecuteOnce(&initialized, khrIcdOsVendorsEnumerate, NULL, NULL);
408 }
409 
410 /*
411  *
412  * Dynamic library loading functions
413  *
414  */
415 
416 // dynamically load a library.  returns NULL on failure
khrIcdOsLibraryLoad(const char * libraryName)417 void *khrIcdOsLibraryLoad(const char *libraryName)
418 {
419     HMODULE hTemp = LoadLibraryExA(libraryName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
420     if (!hTemp && GetLastError() == ERROR_INVALID_PARAMETER)
421     {
422         hTemp = LoadLibraryExA(libraryName, NULL, 0);
423     }
424     if (!hTemp)
425     {
426         KHR_ICD_TRACE("Failed to load driver. Windows error code is %"PRIuDW".\n", GetLastError());
427     }
428     return (void*)hTemp;
429 }
430 
431 // get a function pointer from a loaded library.  returns NULL on failure.
khrIcdOsLibraryGetFunctionAddress(void * library,const char * functionName)432 void *khrIcdOsLibraryGetFunctionAddress(void *library, const char *functionName)
433 {
434     if (!library || !functionName)
435     {
436         return NULL;
437     }
438     return GetProcAddress( (HMODULE)library, functionName);
439 }
440 
441 // unload a library.
khrIcdOsLibraryUnload(void * library)442 void khrIcdOsLibraryUnload(void *library)
443 {
444     FreeLibrary( (HMODULE)library);
445 }
446