1 /*
2 * Copyright © Microsoft Corporation
3 * Copyright (c) 2023 Emil Velikov
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "sysdeps.h"
26 #include "va.h"
27 #include "va_backend.h"
28 #include "va_internal.h"
29 #include "va_trace.h"
30 #include "va_win32.h"
31 #include "compat_win32.h"
32
33 /*
34 * Initialize default driver to the VAOn12 driver implementation
35 * which will be selected when provided with an adapter LUID which
36 * does not have a registered VA driver
37 */
38 const char VAAPI_DEFAULT_DRIVER_NAME[] = "vaon12";
39
40 typedef struct _VADisplayContextWin32 {
41 char registry_driver_name[MAX_PATH];
42 bool registry_driver_available_flag;
43 } VADisplayContextWin32;
44
LoadDriverNameFromRegistry(const LUID * adapter_luid,VADisplayContextWin32 * pWin32Ctx)45 static void LoadDriverNameFromRegistry(const LUID* adapter_luid, VADisplayContextWin32* pWin32Ctx)
46 {
47 HMODULE hGdi32 = LoadLibraryA("gdi32.dll");
48 if (!hGdi32)
49 return;
50
51 D3DKMT_OPENADAPTERFROMLUID OpenArgs = { .AdapterLuid = *adapter_luid };
52 D3DDDI_QUERYREGISTRY_INFO RegistryInfo = {
53 .QueryType = D3DDDI_QUERYREGISTRY_ADAPTERKEY,
54 .QueryFlags.TranslatePath = true,
55 .ValueName = L"VAAPIDriverName",
56 .ValueType = REG_SZ,
57 };
58 D3DDDI_QUERYREGISTRY_INFO *pRegistryInfo = &RegistryInfo;
59 #ifndef _WIN64
60 BOOL isWowProcess = false;
61 if (IsWow64Process(GetCurrentProcess(), &isWowProcess) && isWowProcess)
62 wcscpy(RegistryInfo.ValueName, L"VAAPIDriverNameWow");
63 #endif
64 D3DKMT_QUERYADAPTERINFO QAI = {
65 .Type = KMTQAITYPE_QUERYREGISTRY,
66 .pPrivateDriverData = &RegistryInfo,
67 .PrivateDriverDataSize = sizeof(RegistryInfo),
68 };
69
70 PFND3DKMT_OPENADAPTERFROMLUID pfnOpenAdapterFromLuid = (PFND3DKMT_OPENADAPTERFROMLUID)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromLuid");
71 PFND3DKMT_CLOSEADAPTER pfnCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter");
72 PFND3DKMT_QUERYADAPTERINFO pfnQueryAdapterInfo = (PFND3DKMT_QUERYADAPTERINFO)GetProcAddress(hGdi32, "D3DKMTQueryAdapterInfo");
73 if (!pfnOpenAdapterFromLuid || !pfnCloseAdapter || !pfnQueryAdapterInfo)
74 goto cleanup;
75
76 if (!NT_SUCCESS(pfnOpenAdapterFromLuid(&OpenArgs)))
77 goto cleanup;
78
79 QAI.hAdapter = OpenArgs.hAdapter;
80 if (!NT_SUCCESS(pfnQueryAdapterInfo(&QAI)) ||
81 pRegistryInfo->Status != D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW)
82 goto cleanup;
83
84 size_t RegistryInfoSize = sizeof(RegistryInfo) + RegistryInfo.OutputValueSize;
85 pRegistryInfo = malloc(RegistryInfoSize);
86 if (!pRegistryInfo)
87 goto cleanup;
88
89 memcpy(pRegistryInfo, &RegistryInfo, sizeof(RegistryInfo));
90 QAI.pPrivateDriverData = pRegistryInfo;
91 QAI.PrivateDriverDataSize = RegistryInfoSize;
92 if (!NT_SUCCESS(pfnQueryAdapterInfo(&QAI)) ||
93 pRegistryInfo->Status != D3DDDI_QUERYREGISTRY_STATUS_SUCCESS)
94 goto cleanup;
95
96 if (!WideCharToMultiByte(CP_ACP, 0, pRegistryInfo->OutputString,
97 RegistryInfo.OutputValueSize / sizeof(wchar_t),
98 pWin32Ctx->registry_driver_name,
99 sizeof(pWin32Ctx->registry_driver_name),
100 NULL, NULL))
101 goto cleanup;
102
103 pWin32Ctx->registry_driver_available_flag = true;
104
105 cleanup:
106 if (pRegistryInfo && pRegistryInfo != &RegistryInfo)
107 free(pRegistryInfo);
108 if (pfnCloseAdapter && OpenArgs.hAdapter) {
109 D3DKMT_CLOSEADAPTER Close = { OpenArgs.hAdapter };
110 /* The explicit negation is a no-op, yet required to silence the
111 * Wunused-result warning.
112 */
113 (void) !pfnCloseAdapter(&Close);
114 }
115 FreeLibrary(hGdi32);
116 }
117
va_DisplayContextDestroy(VADisplayContextP pDisplayContext)118 static void va_DisplayContextDestroy(
119 VADisplayContextP pDisplayContext
120 )
121 {
122 if (pDisplayContext == NULL)
123 return;
124
125 if (pDisplayContext->pDriverContext
126 && pDisplayContext->pDriverContext->native_dpy)
127 free(pDisplayContext->pDriverContext->native_dpy);
128
129 free(pDisplayContext->pDriverContext);
130 free(pDisplayContext->opaque);
131 free(pDisplayContext);
132 }
133
va_DisplayContextGetDriverNames(VADisplayContextP pDisplayContext,char ** drivers,unsigned * num_drivers)134 static VAStatus va_DisplayContextGetDriverNames(
135 VADisplayContextP pDisplayContext,
136 char **drivers,
137 unsigned *num_drivers
138 )
139 {
140 const LUID * const adapter = pDisplayContext->pDriverContext->native_dpy;
141 const VADisplayContextWin32 * const pWin32Ctx = pDisplayContext->opaque;
142 unsigned count = 0;
143
144 /* Always prefer the adapter registered driver name as first option */
145 if (adapter && pWin32Ctx->registry_driver_available_flag) {
146 drivers[count] = _strdup(pWin32Ctx->registry_driver_name);
147 count++;
148 }
149 /* Provide the default driver name as a fallback option */
150 if (*num_drivers > count) {
151 drivers[count] = _strdup(VAAPI_DEFAULT_DRIVER_NAME);
152 count++;
153 }
154
155 *num_drivers = count;
156
157 return VA_STATUS_SUCCESS;
158 }
159
vaGetDisplayWin32(const LUID * adapter_luid)160 VADisplay vaGetDisplayWin32(
161 /* Can be null for adapter autoselection in the VA driver */
162 const LUID* adapter_luid
163 )
164 {
165 VADisplayContextP pDisplayContext;
166 VADriverContextP pDriverContext;
167
168 pDisplayContext = va_newDisplayContext();
169 if (!pDisplayContext)
170 return NULL;
171
172 pDisplayContext->vaDestroy = va_DisplayContextDestroy;
173 pDisplayContext->vaGetDriverNames = va_DisplayContextGetDriverNames;
174 pDisplayContext->opaque = calloc(1, sizeof(VADisplayContextWin32));
175 if (!pDisplayContext->opaque) {
176 va_DisplayContextDestroy(pDisplayContext);
177 return NULL;
178 }
179
180 VADisplayContextWin32* pWin32Ctx = (VADisplayContextWin32*) pDisplayContext->opaque;
181 if (adapter_luid) {
182 /* Load the preferred driver name from the driver registry if available */
183 LoadDriverNameFromRegistry(adapter_luid, pWin32Ctx);
184 #ifdef _DEBUG
185 if (pWin32Ctx->registry_driver_available_flag) {
186 fprintf(stderr, "VA_Win32: Found driver %s in the registry for LUID %ld %ld \n", pWin32Ctx->registry_driver_name, adapter_luid->LowPart, adapter_luid->HighPart);
187 } else {
188 fprintf(stderr, "VA_Win32: Couldn't find a driver in the registry for LUID %ld %ld. Using default driver: %s \n", adapter_luid->LowPart, adapter_luid->HighPart, VAAPI_DEFAULT_DRIVER_NAME);
189 }
190 #endif // _DEBUG
191 }
192
193 pDriverContext = va_newDriverContext(pDisplayContext);
194 if (!pDriverContext) {
195 va_DisplayContextDestroy(pDisplayContext);
196 return NULL;
197 }
198
199 pDriverContext->display_type = VA_DISPLAY_WIN32;
200
201 if (adapter_luid) {
202 /* Copy LUID information to driver context */
203 pDriverContext->native_dpy = calloc(1, sizeof(*adapter_luid));
204 memcpy(pDriverContext->native_dpy, adapter_luid, sizeof(*adapter_luid));
205 }
206
207 return (VADisplay)pDisplayContext;
208 }
209