Use QueryAdapter APIs as per MS directives

Update ICD loader to use QueryAdapter APIs
as per MS directives for para-virtualization.

This includes changes to -
1. Order OpenCL platforms consistent with Windows adapters,
   to honor user-settings for multi-adapter machines.
2. Fix OS version detection.
3. Some minor fixes including
    1. Add trace prints at required places.
    2. Fix build failure in getting the address of D3DKMTQueryAdapterInfo.
    3. Fix early return platform enumeartion if there are no entries found for old reg key
       under "HKLM\\SOFTWARE\\Khronos\\OpenCL\\Vendors" to allow add for the entries found
       using DXGK interface and HKR entries.
4. Some other fixes including
    1. Update README.md with WDK dependency information.
    2. Move AddAdapter* call inside for which got misplaced while resolving conflicts.
    3. Free WinAdapter allocations and avoid overriding NULL platform.
5. Fixes based on review comments including -
    1. WinAdapter is reallocated with previous allocation freed.
       However, the final allocation is not freed.
       Fixed the same to free at the end of enumeration.
    2. Fix header file ordering by including windows headers
       inside necessary header files.

Minor fixes to para-virtualization support

Changes to following aspects of para-virtualization changes -
1. Revert platform[0] override when platform is NULL

Fix variable naming, array size and cleanup paths.

Fix changes added as part of para_virtualization support
1. to use camelCase for variable names
2. Use sizeof(element) while calculating array size.
3. Free/unload allocations/libraries during cleanup.

Add return value checks for adapterAdd.

Return boolean result from adapterAdd API
and add checks for success of this API
for better error reporting.

Free adapter names and add check for DXGI interface queries

Add adapterFree to free memory allocated to hold WinAdapter
handles.
Also, check for successful query of DXGIFactory interfaces.

Minor fixes to cleanup Windows para-virtualization changes

Fix indentation and variable name casing.
Fix adapterFree to remove null-pointer check and set it to null after
free.
Add missing header required for adapterAdd.
Add checks for malloced pointers.

Bump up OpenCL ICD Loader version

Bump up OpenCL ICD Loader version
with para-virtualization changes.

Update status based on DXGK and HKR enumearation

khrIcdOsVendorsEnumerate currently does not consider
the status returned by DXGK and HKR enumeration
and returns TRUE only if HKLM regkey enumeration
results in successful adapterAdd.

Fix the returned status to consider successful
adapterAdd from DXGK and HKR enumeration.

Also, set freed adapger pointers to NULL to avoid
potential double-free issue.

Refactor ICD loader windows header files

Add icd_windows.h to include Windows specific
types and functions.
Keep icd_windows_hkr.h and icd_windows_dxgk.h
limited to the HKR and DXGK specific information.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 21da30d..4bafa86 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,6 +51,7 @@
 if (WIN32)
     list (APPEND OPENCL_ICD_LOADER_SOURCES 
         loader/windows/icd_windows.c
+        loader/windows/icd_windows.h
         loader/windows/icd_windows_dxgk.c
         loader/windows/icd_windows_dxgk.h
         loader/windows/icd_windows_envvars.c
diff --git a/README.md b/README.md
index ee8499a..099f3a6 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,11 @@
 By default, the OpenCL ICD Loader will look for OpenCL Headers in the `inc` directory.
 
 By default, the OpenCL ICD Loader on Windows requires the Windows Driver Kit (WDK).
+To build OpenCL ICD Loader with WDK support -
+* Install recent Windows WDK currently at https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk
+
+* Establish environment variable WDK to include directory. Ex: set WDK=C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0
+
 An OpenCL ICD Loader may be built without the Windows Driver Kit using the CMake variable `OPENCL_ICD_LOADER_REQUIRE_WDK`, however this option should be used with caution since it may prevent the OpenCL ICD Loader from enumerating some OpenCL implementations.
 This dependency may be removed in the future.
 
diff --git a/loader/linux/icd_linux.c b/loader/linux/icd_linux.c
index f6ae790..809f095 100644
--- a/loader/linux/icd_linux.c
+++ b/loader/linux/icd_linux.c
@@ -172,4 +172,3 @@
 {
     dlclose(library);
 }
-
diff --git a/loader/windows/OpenCL.rc b/loader/windows/OpenCL.rc
index 356081a..cd6217a 100644
--- a/loader/windows/OpenCL.rc
+++ b/loader/windows/OpenCL.rc
@@ -20,7 +20,7 @@
 
 #define OPENCL_ICD_LOADER_VERSION_MAJOR 2
 #define OPENCL_ICD_LOADER_VERSION_MINOR 2
-#define OPENCL_ICD_LOADER_VERSION_REV   4
+#define OPENCL_ICD_LOADER_VERSION_REV   5
 
 #ifdef RC_INVOKED
 
diff --git a/loader/windows/icd_windows.c b/loader/windows/icd_windows.c
index 9c87817..1350f11 100644
--- a/loader/windows/icd_windows.c
+++ b/loader/windows/icd_windows.c
@@ -17,14 +17,84 @@
  */
 
 #include "icd.h"
+#include "icd_windows.h"
 #include "icd_windows_hkr.h"
 #include "icd_windows_dxgk.h"
 #include <stdio.h>
 #include <windows.h>
 #include <winreg.h>
 
+#include <initguid.h>
+#include <dxgi.h>
+typedef HRESULT (WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID, void **);
+
 static INIT_ONCE initialized = INIT_ONCE_STATIC_INIT;
 
+typedef struct WinAdapter
+{
+    char * szName;
+    LUID luid;
+} WinAdapter;
+
+const LUID ZeroLuid = { 0, 0 };
+
+static WinAdapter* pWinAdapterBegin = NULL;
+static WinAdapter* pWinAdapterEnd = NULL;
+static WinAdapter* pWinAdapterCapacity = NULL;
+
+BOOL adapterAdd(const char* szName, LUID luid)
+{
+    BOOL result = TRUE;
+    if (pWinAdapterEnd == pWinAdapterCapacity)
+    {
+        size_t oldCapacity = pWinAdapterCapacity - pWinAdapterBegin;
+        size_t newCapacity = oldCapacity;
+        if (0 == newCapacity)
+        {
+            newCapacity = 1;
+        }
+        else if(newCapacity < UINT_MAX/2)
+        {
+            newCapacity *= 2;
+        }
+
+        WinAdapter* pNewBegin = malloc(newCapacity * sizeof(*pWinAdapterBegin));
+        if (!pNewBegin)
+            result = FALSE;
+        else
+        {
+            if (pWinAdapterBegin)
+            {
+                memcpy(pNewBegin, pWinAdapterBegin, oldCapacity * sizeof(*pWinAdapterBegin));
+                free(pWinAdapterBegin);
+            }
+            pWinAdapterCapacity = pNewBegin + newCapacity;
+            pWinAdapterEnd = pNewBegin + oldCapacity;
+            pWinAdapterBegin = pNewBegin;
+        }
+    }
+    if (pWinAdapterEnd != pWinAdapterCapacity)
+    {
+        size_t nameLen = (strlen(szName) + 1)*sizeof(szName[0]);
+        pWinAdapterEnd->szName = malloc(nameLen);
+        if (!pWinAdapterEnd->szName)
+            result = FALSE;
+        else
+        {
+            memcpy(pWinAdapterEnd->szName, szName, nameLen);
+            pWinAdapterEnd->luid = luid;
+            ++pWinAdapterEnd;
+        }
+    }
+    return result;
+}
+
+void adapterFree(WinAdapter *pWinAdapter)
+{
+    free(pWinAdapter->szName);
+    pWinAdapter->szName = NULL;
+}
+
 /*
  * 
  * Vendor enumeration functions
@@ -36,16 +106,19 @@
 BOOL CALLBACK khrIcdOsVendorsEnumerate(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
 {
     LONG result;
+    BOOL status = FALSE;
     const char* platformsName = "SOFTWARE\\Khronos\\OpenCL\\Vendors";
     HKEY platformsKey = NULL;
     DWORD dwIndex;
 
     khrIcdVendorsEnumerateEnv();
 
-    if (!khrIcdOsVendorsEnumerateDXGK())
+    status |= khrIcdOsVendorsEnumerateDXGK();
+    if (!status)
     {
         KHR_ICD_TRACE("Failed to load via DXGK interface on RS4, continuing\n");
-        if (!khrIcdOsVendorsEnumerateHKR())
+        status |= khrIcdOsVendorsEnumerateHKR();
+        if (!status)
         {
             KHR_ICD_TRACE("Failed to enumerate HKR entries, continuing\n");
         }
@@ -103,19 +176,66 @@
                 KHR_ICD_TRACE("Value not zero, skipping\n");
                 continue;
             }
-
             // add the library
-            khrIcdVendorAdd(cszLibraryName);
-        }
-
-        result = RegCloseKey(platformsKey);
-        if (ERROR_SUCCESS != result)
-        {
-            KHR_ICD_TRACE("Failed to close platforms key %s, ignoring\n", platformsName);
+            status |= adapterAdd(cszLibraryName, ZeroLuid);
         }
     }
 
-    return TRUE;
+    // Add adapters according to DXGI's preference order
+    HMODULE hDXGI = LoadLibrary("dxgi.dll");
+    if (hDXGI)
+    {
+        IDXGIFactory* pFactory = NULL;
+        PFN_CREATE_DXGI_FACTORY pCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY)GetProcAddress(hDXGI, "CreateDXGIFactory");
+        if (pCreateDXGIFactory)
+        {
+            HRESULT hr = pCreateDXGIFactory(&IID_IDXGIFactory, &pFactory);
+            if (SUCCEEDED(hr))
+            {
+                UINT i = 0;
+                IDXGIAdapter* pAdapter = NULL;
+                while (SUCCEEDED(pFactory->lpVtbl->EnumAdapters(pFactory, i++, &pAdapter)))
+                {
+                    DXGI_ADAPTER_DESC AdapterDesc;
+                    if (SUCCEEDED(pAdapter->lpVtbl->GetDesc(pAdapter, &AdapterDesc)))
+                    {
+                        for (WinAdapter* iterAdapter = pWinAdapterBegin; iterAdapter != pWinAdapterEnd; ++iterAdapter)
+                        {
+                            if (iterAdapter->luid.LowPart == AdapterDesc.AdapterLuid.LowPart
+                                && iterAdapter->luid.HighPart == AdapterDesc.AdapterLuid.HighPart)
+                            {
+                                khrIcdVendorAdd(iterAdapter->szName);
+                                break;
+                            }
+                        }
+                    }
+
+                    pAdapter->lpVtbl->Release(pAdapter);
+                }
+                pFactory->lpVtbl->Release(pFactory);
+            }
+            FreeLibrary(hDXGI);
+        }
+    }
+
+    // Go through the list again, putting any remaining adapters at the end of the list in an undefined order
+    for (WinAdapter* iterAdapter = pWinAdapterBegin; iterAdapter != pWinAdapterEnd; ++iterAdapter)
+    {
+        khrIcdVendorAdd(iterAdapter->szName);
+        adapterFree(iterAdapter);
+    }
+
+    free(pWinAdapterBegin);
+    pWinAdapterBegin = NULL;
+    pWinAdapterEnd = NULL;
+    pWinAdapterCapacity = NULL;
+
+    result = RegCloseKey(platformsKey);
+    if (ERROR_SUCCESS != result)
+    {
+        KHR_ICD_TRACE("Failed to close platforms key %s, ignoring\n", platformsName);
+    }
+    return status;
 }
 
 // go through the list of vendors only once
@@ -151,4 +271,3 @@
 {
     FreeLibrary( (HMODULE)library);
 }
-
diff --git a/loader/windows/icd_windows.h b/loader/windows/icd_windows.h
new file mode 100644
index 0000000..48bbf13
--- /dev/null
+++ b/loader/windows/icd_windows.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+#include <stdbool.h>
+#include <windows.h>
+
+extern const LUID ZeroLuid;
+
+BOOL adapterAdd(const char* szName, LUID luid);
+
+// Do not free the memory returned by this function.
+const char* getOpenCLRegKeyName(void);
diff --git a/loader/windows/icd_windows_dxgk.c b/loader/windows/icd_windows_dxgk.c
index 8cc3fb9..088abca 100644
--- a/loader/windows/icd_windows_dxgk.c
+++ b/loader/windows/icd_windows_dxgk.c
@@ -35,18 +35,23 @@
 bool khrIcdOsVendorsEnumerateDXGK(void)
 {
     bool ret = false;
+    int result = 0;
 #if defined(OPENCL_ICD_LOADER_REQUIRE_WDK)
 #if defined(DXGKDDI_INTERFACE_VERSION_WDDM2_4) && (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WDDM2_4)
+    // Get handle to GDI Runtime
+    HMODULE h = LoadLibrary("gdi32.dll");
+    if (h == NULL)
+        return ret;
+
+    if(GetProcAddress((HMODULE)h, "D3DKMTSubmitPresentBltToHwQueue")) // OS Version check
     {
         D3DKMT_ADAPTERINFO* pAdapterInfo = NULL;
         D3DKMT_ENUMADAPTERS2 EnumAdapters;
         NTSTATUS Status = STATUS_SUCCESS;
 
-        // Get handle to GDI Runtime
-        HMODULE h = LoadLibrary("gdi32.dll");
-        KHR_ICD_ASSERT(h != NULL);
-
         char cszLibraryName[MAX_PATH] = { 0 };
+        EnumAdapters.NumAdapters = 0;
+        EnumAdapters.pAdapters = NULL;
         PFND3DKMT_ENUMADAPTERS2 pEnumAdapters2 = (PFND3DKMT_ENUMADAPTERS2)GetProcAddress((HMODULE)h, "D3DKMTEnumAdapters2");
         if (!pEnumAdapters2)
         {
@@ -83,47 +88,50 @@
             KHR_ICD_TRACE("D3DKMT_ENUMADAPTERS2 status != SUCCESS\n");
             goto out;
         }
+        const char* cszOpenCLRegKeyName = getOpenCLRegKeyName();
+        const int szOpenCLRegKeyName = (int)(strlen(cszOpenCLRegKeyName) + 1)*sizeof(cszOpenCLRegKeyName[0]);
         for (UINT AdapterIndex = 0; AdapterIndex < EnumAdapters.NumAdapters; AdapterIndex++)
         {
-            D3DDDI_QUERYREGISTRY_INFO QueryArgs = {0};
-            D3DDDI_QUERYREGISTRY_INFO* pQueryArgs = &QueryArgs;
+            D3DDDI_QUERYREGISTRY_INFO queryArgs = {0};
+            D3DDDI_QUERYREGISTRY_INFO* pQueryArgs = &queryArgs;
             D3DDDI_QUERYREGISTRY_INFO* pQueryBuffer = NULL;
-            QueryArgs.QueryType = D3DDDI_QUERYREGISTRY_ADAPTERKEY;
-            QueryArgs.QueryFlags.TranslatePath = TRUE;
-            QueryArgs.ValueType = REG_SZ;
-#ifdef _WIN64
-            wcscpy_s(QueryArgs.ValueName, ARRAYSIZE(L"OpenCLDriverName"), L"OpenCLDriverName");
-#else
-            // There is no WOW prefix for 32bit Windows hence make a specific check
-            BOOL is_wow64;
-            if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)
+            queryArgs.QueryType = D3DDDI_QUERYREGISTRY_ADAPTERKEY;
+            queryArgs.QueryFlags.TranslatePath = TRUE;
+            queryArgs.ValueType = REG_SZ;
+            result = MultiByteToWideChar(
+                CP_ACP,
+                0,
+                cszOpenCLRegKeyName,
+                szOpenCLRegKeyName,
+                queryArgs.ValueName,
+                ARRAYSIZE(queryArgs.ValueName));
+            if (!result)
             {
-                wcscpy_s(QueryArgs.ValueName, ARRAYSIZE(L"OpenCLDriverNameWow"), L"OpenCLDriverNameWow");
+                KHR_ICD_TRACE("MultiByteToWideChar status != SUCCESS\n");
+                continue;
             }
-            else
-            {
-                wcscpy_s(QueryArgs.ValueName, ARRAYSIZE(L"OpenCLDriverName"), L"OpenCLDriverName");
-            }
-#endif
-            D3DKMT_QUERYADAPTERINFO QueryAdapterInfo = {0};
-            QueryAdapterInfo.hAdapter = pAdapterInfo[AdapterIndex].hAdapter;
-            QueryAdapterInfo.Type = KMTQAITYPE_QUERYREGISTRY;
-            QueryAdapterInfo.pPrivateDriverData = &QueryArgs;
-            QueryAdapterInfo.PrivateDriverDataSize = sizeof(QueryArgs);
-            Status = D3DKMTQueryAdapterInfo(&QueryAdapterInfo);
+            D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {0};
+            queryAdapterInfo.hAdapter = pAdapterInfo[AdapterIndex].hAdapter;
+            queryAdapterInfo.Type = KMTQAITYPE_QUERYREGISTRY;
+            queryAdapterInfo.pPrivateDriverData = &queryArgs;
+            queryAdapterInfo.PrivateDriverDataSize = sizeof(queryArgs);
+            Status = D3DKMTQueryAdapterInfo(&queryAdapterInfo);
             if (!NT_SUCCESS(Status))
             {
-                KHR_ICD_TRACE("D3DKMT_QUERYADAPTERINFO status != SUCCESS\n");
-                goto out;
+                // Continue trying to get as much info on each adapter as possible.
+                // It's too late to return FALSE and claim WDDM2_4 enumeration is not available here.
+                continue;
             }
             if (NT_SUCCESS(Status) && pQueryArgs->Status == D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW)
             {
-                ULONG QueryBufferSize = sizeof(D3DDDI_QUERYREGISTRY_INFO) + QueryArgs.OutputValueSize;
-                pQueryBuffer = (D3DDDI_QUERYREGISTRY_INFO*)malloc(QueryBufferSize);
-                memcpy(pQueryBuffer, &QueryArgs, sizeof(D3DDDI_QUERYREGISTRY_INFO));
-                QueryAdapterInfo.pPrivateDriverData = pQueryBuffer;
-                QueryAdapterInfo.PrivateDriverDataSize = QueryBufferSize;
-                Status = D3DKMTQueryAdapterInfo(&QueryAdapterInfo);
+                ULONG queryBufferSize = sizeof(D3DDDI_QUERYREGISTRY_INFO) + queryArgs.OutputValueSize;
+                pQueryBuffer = (D3DDDI_QUERYREGISTRY_INFO*)malloc(queryBufferSize);
+                if (pQueryBuffer == NULL)
+                    continue;
+                memcpy(pQueryBuffer, &queryArgs, sizeof(D3DDDI_QUERYREGISTRY_INFO));
+                queryAdapterInfo.pPrivateDriverData = pQueryBuffer;
+                queryAdapterInfo.PrivateDriverDataSize = queryBufferSize;
+                Status = D3DKMTQueryAdapterInfo(&queryAdapterInfo);
                 pQueryArgs = pQueryBuffer;
             }
             if (NT_SUCCESS(Status) && pQueryArgs->Status == D3DDDI_QUERYREGISTRY_STATUS_SUCCESS)
@@ -133,7 +141,7 @@
                 {
                     size_t len = wcstombs(cszLibraryName, pWchar, sizeof(cszLibraryName));
                     KHR_ICD_ASSERT(len == (sizeof(cszLibraryName) - 1));
-                    khrIcdVendorAdd(cszLibraryName);
+                    ret |= adapterAdd(cszLibraryName, pAdapterInfo[AdapterIndex].AdapterLuid);
                 }
             }
             else if (Status == STATUS_INVALID_PARAMETER && pQueryArgs->Status == D3DDDI_QUERYREGISTRY_STATUS_FAIL)
@@ -143,11 +151,12 @@
             }
             free(pQueryBuffer);
         }
-        ret = true;
 out:
       free(pAdapterInfo);
-      FreeLibrary(h);
     }
+
+    FreeLibrary(h);
+
 #endif
 #endif
     return ret;
diff --git a/loader/windows/icd_windows_dxgk.h b/loader/windows/icd_windows_dxgk.h
index f6cb65d..34bad64 100644
--- a/loader/windows/icd_windows_dxgk.h
+++ b/loader/windows/icd_windows_dxgk.h
@@ -17,5 +17,6 @@
  */
 
 #include <stdbool.h>
+#include "icd_windows.h"
 
 bool khrIcdOsVendorsEnumerateDXGK(void);
diff --git a/loader/windows/icd_windows_hkr.c b/loader/windows/icd_windows_hkr.c
index 3387181..a323312 100644
--- a/loader/windows/icd_windows_hkr.c
+++ b/loader/windows/icd_windows_hkr.c
@@ -19,6 +19,7 @@
 #include "icd.h"
 #include "icd_windows_hkr.h"
 #include <windows.h>
+#include "icd_windows_dxgk.h"
 #include <cfgmgr32.h>
 #include <assert.h>
 #include <stdbool.h>
@@ -51,7 +52,7 @@
 #endif
 
 // Do not free the memory returned by this function.
-static const char* GetOpenCLRegKeyName(void)
+const char* getOpenCLRegKeyName(void)
 {
 #ifdef _WIN64
     return OPENCL_REG_SUB_KEY;
@@ -96,7 +97,7 @@
     {
         result = RegQueryValueExA(
             hkey,
-            GetOpenCLRegKeyName(),
+            getOpenCLRegKeyName(),
             NULL,
             &dwLibraryNameType,
             NULL,
@@ -117,7 +118,7 @@
 
         result = RegQueryValueExA(
             hkey,
-            GetOpenCLRegKeyName(),
+            getOpenCLRegKeyName(),
             NULL,
             &dwLibraryNameType,
             (LPBYTE)cszOclPath,
@@ -136,9 +137,7 @@
 
         KHR_ICD_TRACE("    Path: %s\n", cszOclPath);
 
-        khrIcdVendorAdd(cszOclPath);
-
-        bRet = true;
+        bRet |= adapterAdd(cszOclPath, ZeroLuid);
     }
 
 out:
diff --git a/loader/windows/icd_windows_hkr.h b/loader/windows/icd_windows_hkr.h
index 698fe5a..e0db713 100644
--- a/loader/windows/icd_windows_hkr.h
+++ b/loader/windows/icd_windows_hkr.h
@@ -17,5 +17,6 @@
  */
 
 #include <stdbool.h>
+#include "icd_windows.h"
 
 bool khrIcdOsVendorsEnumerateHKR(void);