/* * Copyright © Microsoft Corporation * Copyright (c) 2023 Emil Velikov * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "sysdeps.h" #include "va.h" #include "va_backend.h" #include "va_internal.h" #include "va_trace.h" #include "va_win32.h" #include "compat_win32.h" /* * Initialize default driver to the VAOn12 driver implementation * which will be selected when provided with an adapter LUID which * does not have a registered VA driver */ const char VAAPI_DEFAULT_DRIVER_NAME[] = "vaon12"; typedef struct _VADisplayContextWin32 { char registry_driver_name[MAX_PATH]; bool registry_driver_available_flag; } VADisplayContextWin32; static void LoadDriverNameFromRegistry(const LUID* adapter_luid, VADisplayContextWin32* pWin32Ctx) { HMODULE hGdi32 = LoadLibraryA("gdi32.dll"); if (!hGdi32) return; D3DKMT_OPENADAPTERFROMLUID OpenArgs = { .AdapterLuid = *adapter_luid }; D3DDDI_QUERYREGISTRY_INFO RegistryInfo = { .QueryType = D3DDDI_QUERYREGISTRY_ADAPTERKEY, .QueryFlags.TranslatePath = true, .ValueName = L"VAAPIDriverName", .ValueType = REG_SZ, }; D3DDDI_QUERYREGISTRY_INFO *pRegistryInfo = &RegistryInfo; #ifndef _WIN64 BOOL isWowProcess = false; if (IsWow64Process(GetCurrentProcess(), &isWowProcess) && isWowProcess) wcscpy(RegistryInfo.ValueName, L"VAAPIDriverNameWow"); #endif D3DKMT_QUERYADAPTERINFO QAI = { .Type = KMTQAITYPE_QUERYREGISTRY, .pPrivateDriverData = &RegistryInfo, .PrivateDriverDataSize = sizeof(RegistryInfo), }; PFND3DKMT_OPENADAPTERFROMLUID pfnOpenAdapterFromLuid = (PFND3DKMT_OPENADAPTERFROMLUID)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromLuid"); PFND3DKMT_CLOSEADAPTER pfnCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter"); PFND3DKMT_QUERYADAPTERINFO pfnQueryAdapterInfo = (PFND3DKMT_QUERYADAPTERINFO)GetProcAddress(hGdi32, "D3DKMTQueryAdapterInfo"); if (!pfnOpenAdapterFromLuid || !pfnCloseAdapter || !pfnQueryAdapterInfo) goto cleanup; if (!NT_SUCCESS(pfnOpenAdapterFromLuid(&OpenArgs))) goto cleanup; QAI.hAdapter = OpenArgs.hAdapter; if (!NT_SUCCESS(pfnQueryAdapterInfo(&QAI)) || pRegistryInfo->Status != D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW) goto cleanup; size_t RegistryInfoSize = sizeof(RegistryInfo) + RegistryInfo.OutputValueSize; pRegistryInfo = malloc(RegistryInfoSize); if (!pRegistryInfo) goto cleanup; memcpy(pRegistryInfo, &RegistryInfo, sizeof(RegistryInfo)); QAI.pPrivateDriverData = pRegistryInfo; QAI.PrivateDriverDataSize = RegistryInfoSize; if (!NT_SUCCESS(pfnQueryAdapterInfo(&QAI)) || pRegistryInfo->Status != D3DDDI_QUERYREGISTRY_STATUS_SUCCESS) goto cleanup; if (!WideCharToMultiByte(CP_ACP, 0, pRegistryInfo->OutputString, RegistryInfo.OutputValueSize / sizeof(wchar_t), pWin32Ctx->registry_driver_name, sizeof(pWin32Ctx->registry_driver_name), NULL, NULL)) goto cleanup; pWin32Ctx->registry_driver_available_flag = true; cleanup: if (pRegistryInfo && pRegistryInfo != &RegistryInfo) free(pRegistryInfo); if (pfnCloseAdapter && OpenArgs.hAdapter) { D3DKMT_CLOSEADAPTER Close = { OpenArgs.hAdapter }; /* The explicit negation is a no-op, yet required to silence the * Wunused-result warning. */ (void) !pfnCloseAdapter(&Close); } FreeLibrary(hGdi32); } static void va_DisplayContextDestroy( VADisplayContextP pDisplayContext ) { if (pDisplayContext == NULL) return; if (pDisplayContext->pDriverContext && pDisplayContext->pDriverContext->native_dpy) free(pDisplayContext->pDriverContext->native_dpy); free(pDisplayContext->pDriverContext); free(pDisplayContext->opaque); free(pDisplayContext); } static VAStatus va_DisplayContextGetDriverNames( VADisplayContextP pDisplayContext, char **drivers, unsigned *num_drivers ) { const LUID * const adapter = pDisplayContext->pDriverContext->native_dpy; const VADisplayContextWin32 * const pWin32Ctx = pDisplayContext->opaque; unsigned count = 0; /* Always prefer the adapter registered driver name as first option */ if (adapter && pWin32Ctx->registry_driver_available_flag) { drivers[count] = _strdup(pWin32Ctx->registry_driver_name); count++; } /* Provide the default driver name as a fallback option */ if (*num_drivers > count) { drivers[count] = _strdup(VAAPI_DEFAULT_DRIVER_NAME); count++; } *num_drivers = count; return VA_STATUS_SUCCESS; } VADisplay vaGetDisplayWin32( /* Can be null for adapter autoselection in the VA driver */ const LUID* adapter_luid ) { VADisplayContextP pDisplayContext; VADriverContextP pDriverContext; pDisplayContext = va_newDisplayContext(); if (!pDisplayContext) return NULL; pDisplayContext->vaDestroy = va_DisplayContextDestroy; pDisplayContext->vaGetDriverNames = va_DisplayContextGetDriverNames; pDisplayContext->opaque = calloc(1, sizeof(VADisplayContextWin32)); if (!pDisplayContext->opaque) { va_DisplayContextDestroy(pDisplayContext); return NULL; } VADisplayContextWin32* pWin32Ctx = (VADisplayContextWin32*) pDisplayContext->opaque; if (adapter_luid) { /* Load the preferred driver name from the driver registry if available */ LoadDriverNameFromRegistry(adapter_luid, pWin32Ctx); #ifdef _DEBUG if (pWin32Ctx->registry_driver_available_flag) { 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); } else { 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); } #endif // _DEBUG } pDriverContext = va_newDriverContext(pDisplayContext); if (!pDriverContext) { va_DisplayContextDestroy(pDisplayContext); return NULL; } pDriverContext->display_type = VA_DISPLAY_WIN32; if (adapter_luid) { /* Copy LUID information to driver context */ pDriverContext->native_dpy = calloc(1, sizeof(*adapter_luid)); memcpy(pDriverContext->native_dpy, adapter_luid, sizeof(*adapter_luid)); } return (VADisplay)pDisplayContext; }