Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

win32/driver loader: Allow backends to load drivers from registry path #715

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 156 additions & 147 deletions va/va.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,12 @@
#include <string.h>
#if defined(_WIN32)
#include "compat_win32.h"
#define DRIVER_EXTENSION "_drv_video.dll"
#define DRIVER_PATH_STRING "%s\\%s%s"
#define ENV_VAR_SEPARATOR ";"
#else
#include <dlfcn.h>
#include <unistd.h>
#define DRIVER_EXTENSION "_drv_video.so"
#define DRIVER_PATH_STRING "%s/%s%s"
#define DRIVER_PATH_STRING "%s/%s%s.so"
#define ENV_VAR_SEPARATOR ":"
#endif
#ifdef ANDROID
Expand Down Expand Up @@ -353,26 +351,163 @@ va_getDriverInitName(char *name, int namelen, int major, int minor)
return ret > 0 && ret < namelen;
}

static char *va_getDriverPath(const char *driver_dir, const char *driver_name)
static char *va_getDriverPath(const char *driver_dir, const char *driver_name, bool use_suffix)
{
int n = snprintf(0, 0, DRIVER_PATH_STRING, driver_dir, driver_name, DRIVER_EXTENSION);
int n = snprintf(0, 0, DRIVER_PATH_STRING, driver_dir, driver_name, use_suffix ? "_drv_video" : "");
if (n < 0)
return NULL;
char *driver_path = (char *) malloc(n + 1);
if (!driver_path)
return NULL;
n = snprintf(driver_path, n + 1, DRIVER_PATH_STRING,
driver_dir, driver_name, DRIVER_EXTENSION);
driver_dir, driver_name, use_suffix ? "_drv_video" : "");
if (n < 0) {
free(driver_path);
return NULL;
}
return driver_path;
}

static VAStatus va_openDriver(VADisplay dpy, char *driver_name)
static VAStatus va_openDriverFromPath(VADisplay dpy, char *driver_path)
{
VADriverContextP ctx = CTX(dpy);
VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN;
va_infoMessage(dpy, "Trying to open %s\n", driver_path);
#if defined(RTLD_NODELETE) && !defined(ANDROID)
void* handle = dlopen(driver_path, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
#else
void* handle = dlopen(driver_path, RTLD_NOW | RTLD_GLOBAL);
#endif
if (!handle) {
/* Don't give errors for non-existing files */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't give errors for non-existing files

why :). Besides that's not true since you will return default VA_STATUS_ERROR_UNKNOWN defined above. So, maybe we need the following for simplicity:

    void* handle = dlopen(driver_path, RTLD_NOW | RTLD_GLOBAL);
    if (!handle) {
        if (0 == access(driver_path, F_OK))
            va_errorMessage(dpy, "dlopen of %s failed: %s\n", driver_path, dlerror());
        return vaStatus;
    }

    // other code....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd happy to make that change to make it cleaner/simpler, but that is the already existing behavior in va.c in master too, I didn't want to make any functional changes here other than extracting the va_OpenDriverFromPath function. Maybe this behaviour has a historical reason for being like this? The last change to this line was 16 years ago :/

if (0 == access(driver_path, F_OK))
va_errorMessage(dpy, "dlopen of %s failed: %s\n", driver_path, dlerror());
} else {
VADriverInit init_func = NULL;
char init_func_s[256];
int i;

struct {
int major;
int minor;
} compatible_versions[VA_MINOR_VERSION + 2];
for (i = 0; i <= VA_MINOR_VERSION; i ++) {
compatible_versions[i].major = VA_MAJOR_VERSION;
compatible_versions[i].minor = VA_MINOR_VERSION - i;
}
compatible_versions[i].major = -1;
compatible_versions[i].minor = -1;

for (i = 0; compatible_versions[i].major >= 0; i++) {
if (va_getDriverInitName(init_func_s, sizeof(init_func_s),
compatible_versions[i].major,
compatible_versions[i].minor)) {
init_func = (VADriverInit)dlsym(handle, init_func_s);
if (init_func) {
va_infoMessage(dpy, "Found init function %s\n", init_func_s);
break;
}
}
}

if (compatible_versions[i].major < 0) {
va_errorMessage(dpy, "%s has no function %s\n",
driver_path, init_func_s);
dlclose(handle);
} else {
struct VADriverVTable *vtable = ctx->vtable;
struct VADriverVTableVPP *vtable_vpp = ctx->vtable_vpp;
struct VADriverVTableProt *vtable_prot = ctx->vtable_prot;

vaStatus = VA_STATUS_SUCCESS;
if (!vtable) {
vtable = calloc(1, sizeof(*vtable));
if (!vtable)
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->vtable = vtable;

if (!vtable_vpp) {
vtable_vpp = calloc(1, sizeof(*vtable_vpp));
if (vtable_vpp)
vtable_vpp->version = VA_DRIVER_VTABLE_VPP_VERSION;
else
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->vtable_vpp = vtable_vpp;

if (!vtable_prot) {
vtable_prot = calloc(1, sizeof(*vtable_prot));
if (vtable_prot)
vtable_prot->version = VA_DRIVER_VTABLE_PROT_VERSION;
else
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->vtable_prot = vtable_prot;

if (init_func && VA_STATUS_SUCCESS == vaStatus)
vaStatus = (*init_func)(ctx);

if (VA_STATUS_SUCCESS == vaStatus) {
CHECK_MAXIMUM(vaStatus, ctx, profiles);
CHECK_MAXIMUM(vaStatus, ctx, entrypoints);
CHECK_MAXIMUM(vaStatus, ctx, attributes);
CHECK_MAXIMUM(vaStatus, ctx, image_formats);
CHECK_MAXIMUM(vaStatus, ctx, subpic_formats);
CHECK_STRING(vaStatus, ctx, vendor);
CHECK_VTABLE(vaStatus, ctx, Terminate);
CHECK_VTABLE(vaStatus, ctx, QueryConfigProfiles);
CHECK_VTABLE(vaStatus, ctx, QueryConfigEntrypoints);
CHECK_VTABLE(vaStatus, ctx, QueryConfigAttributes);
CHECK_VTABLE(vaStatus, ctx, CreateConfig);
CHECK_VTABLE(vaStatus, ctx, DestroyConfig);
CHECK_VTABLE(vaStatus, ctx, GetConfigAttributes);
CHECK_VTABLE(vaStatus, ctx, CreateSurfaces);
CHECK_VTABLE(vaStatus, ctx, DestroySurfaces);
CHECK_VTABLE(vaStatus, ctx, CreateContext);
CHECK_VTABLE(vaStatus, ctx, DestroyContext);
CHECK_VTABLE(vaStatus, ctx, CreateBuffer);
CHECK_VTABLE(vaStatus, ctx, BufferSetNumElements);
CHECK_VTABLE(vaStatus, ctx, MapBuffer);
CHECK_VTABLE(vaStatus, ctx, UnmapBuffer);
CHECK_VTABLE(vaStatus, ctx, DestroyBuffer);
CHECK_VTABLE(vaStatus, ctx, BeginPicture);
CHECK_VTABLE(vaStatus, ctx, RenderPicture);
CHECK_VTABLE(vaStatus, ctx, EndPicture);
CHECK_VTABLE(vaStatus, ctx, SyncSurface);
CHECK_VTABLE(vaStatus, ctx, QuerySurfaceStatus);
CHECK_VTABLE(vaStatus, ctx, QueryImageFormats);
CHECK_VTABLE(vaStatus, ctx, CreateImage);
CHECK_VTABLE(vaStatus, ctx, DeriveImage);
CHECK_VTABLE(vaStatus, ctx, DestroyImage);
CHECK_VTABLE(vaStatus, ctx, SetImagePalette);
CHECK_VTABLE(vaStatus, ctx, GetImage);
CHECK_VTABLE(vaStatus, ctx, PutImage);
CHECK_VTABLE(vaStatus, ctx, QuerySubpictureFormats);
CHECK_VTABLE(vaStatus, ctx, CreateSubpicture);
CHECK_VTABLE(vaStatus, ctx, DestroySubpicture);
CHECK_VTABLE(vaStatus, ctx, SetSubpictureImage);
CHECK_VTABLE(vaStatus, ctx, SetSubpictureChromakey);
CHECK_VTABLE(vaStatus, ctx, SetSubpictureGlobalAlpha);
CHECK_VTABLE(vaStatus, ctx, AssociateSubpicture);
CHECK_VTABLE(vaStatus, ctx, DeassociateSubpicture);
CHECK_VTABLE(vaStatus, ctx, QueryDisplayAttributes);
CHECK_VTABLE(vaStatus, ctx, GetDisplayAttributes);
CHECK_VTABLE(vaStatus, ctx, SetDisplayAttributes);
}
if (VA_STATUS_SUCCESS != vaStatus) {
va_errorMessage(dpy, "%s init failed\n", driver_path);
dlclose(handle);
}
if (VA_STATUS_SUCCESS == vaStatus)
ctx->handle = handle;
}
}
return vaStatus;
}

static VAStatus va_openDriver(VADisplay dpy, char *driver_name)
{
VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN;
char *search_path = NULL;
char *saveptr;
Expand All @@ -392,150 +527,20 @@ static VAStatus va_openDriver(VADisplay dpy, char *driver_name)
}
driver_dir = strtok_r(search_path, ENV_VAR_SEPARATOR, &saveptr);
while (driver_dir) {
void *handle = NULL;
char *driver_path = va_getDriverPath(driver_dir, driver_name);
if (!driver_path) {
va_errorMessage(dpy, "%s L%d Out of memory\n",
__FUNCTION__, __LINE__);
free(search_path);
return VA_STATUS_ERROR_ALLOCATION_FAILED;
}

va_infoMessage(dpy, "Trying to open %s\n", driver_path);
#if defined(RTLD_NODELETE) && !defined(ANDROID)
handle = dlopen(driver_path, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
#else
handle = dlopen(driver_path, RTLD_NOW | RTLD_GLOBAL);
#endif
if (!handle) {
/* Don't give errors for non-existing files */
if (0 == access(driver_path, F_OK))
va_errorMessage(dpy, "dlopen of %s failed: %s\n", driver_path, dlerror());
} else {
VADriverInit init_func = NULL;
char init_func_s[256];
int i;

struct {
int major;
int minor;
} compatible_versions[VA_MINOR_VERSION + 2];
for (i = 0; i <= VA_MINOR_VERSION; i ++) {
compatible_versions[i].major = VA_MAJOR_VERSION;
compatible_versions[i].minor = VA_MINOR_VERSION - i;
}
compatible_versions[i].major = -1;
compatible_versions[i].minor = -1;

for (i = 0; compatible_versions[i].major >= 0; i++) {
if (va_getDriverInitName(init_func_s, sizeof(init_func_s),
compatible_versions[i].major,
compatible_versions[i].minor)) {
init_func = (VADriverInit)dlsym(handle, init_func_s);
if (init_func) {
va_infoMessage(dpy, "Found init function %s\n", init_func_s);
break;
}
}
for (int use_suffix = 1; use_suffix >= 0; use_suffix--) {
char *driver_path = va_getDriverPath(driver_dir, driver_name, use_suffix);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this construct vaon12_drv_video_drv_video with the changed default driver name for windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the first attempt will be vaon12_drv_video_drv_video and then fallback to vaon12_drv_video (no added suffix). This is done like this (and in this order) to prevent any functional changes that may break other backends that follow the strict name suffix on Linux. While this adds an extra attempt to the loading on Windows, I think it's less invasive than trying without suffix first (that would add an extra attempt to all other backends before finding their driver files on Linux)

if (!driver_path) {
va_errorMessage(dpy, "%s L%d Out of memory\n",
__FUNCTION__, __LINE__);
free(search_path);
return VA_STATUS_ERROR_ALLOCATION_FAILED;
}

if (compatible_versions[i].major < 0) {
va_errorMessage(dpy, "%s has no function %s\n",
driver_path, init_func_s);
dlclose(handle);
} else {
struct VADriverVTable *vtable = ctx->vtable;
struct VADriverVTableVPP *vtable_vpp = ctx->vtable_vpp;
struct VADriverVTableProt *vtable_prot = ctx->vtable_prot;

vaStatus = VA_STATUS_SUCCESS;
if (!vtable) {
vtable = calloc(1, sizeof(*vtable));
if (!vtable)
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->vtable = vtable;

if (!vtable_vpp) {
vtable_vpp = calloc(1, sizeof(*vtable_vpp));
if (vtable_vpp)
vtable_vpp->version = VA_DRIVER_VTABLE_VPP_VERSION;
else
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->vtable_vpp = vtable_vpp;

if (!vtable_prot) {
vtable_prot = calloc(1, sizeof(*vtable_prot));
if (vtable_prot)
vtable_prot->version = VA_DRIVER_VTABLE_PROT_VERSION;
else
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->vtable_prot = vtable_prot;

if (init_func && VA_STATUS_SUCCESS == vaStatus)
vaStatus = (*init_func)(ctx);

if (VA_STATUS_SUCCESS == vaStatus) {
CHECK_MAXIMUM(vaStatus, ctx, profiles);
CHECK_MAXIMUM(vaStatus, ctx, entrypoints);
CHECK_MAXIMUM(vaStatus, ctx, attributes);
CHECK_MAXIMUM(vaStatus, ctx, image_formats);
CHECK_MAXIMUM(vaStatus, ctx, subpic_formats);
CHECK_STRING(vaStatus, ctx, vendor);
CHECK_VTABLE(vaStatus, ctx, Terminate);
CHECK_VTABLE(vaStatus, ctx, QueryConfigProfiles);
CHECK_VTABLE(vaStatus, ctx, QueryConfigEntrypoints);
CHECK_VTABLE(vaStatus, ctx, QueryConfigAttributes);
CHECK_VTABLE(vaStatus, ctx, CreateConfig);
CHECK_VTABLE(vaStatus, ctx, DestroyConfig);
CHECK_VTABLE(vaStatus, ctx, GetConfigAttributes);
CHECK_VTABLE(vaStatus, ctx, CreateSurfaces);
CHECK_VTABLE(vaStatus, ctx, DestroySurfaces);
CHECK_VTABLE(vaStatus, ctx, CreateContext);
CHECK_VTABLE(vaStatus, ctx, DestroyContext);
CHECK_VTABLE(vaStatus, ctx, CreateBuffer);
CHECK_VTABLE(vaStatus, ctx, BufferSetNumElements);
CHECK_VTABLE(vaStatus, ctx, MapBuffer);
CHECK_VTABLE(vaStatus, ctx, UnmapBuffer);
CHECK_VTABLE(vaStatus, ctx, DestroyBuffer);
CHECK_VTABLE(vaStatus, ctx, BeginPicture);
CHECK_VTABLE(vaStatus, ctx, RenderPicture);
CHECK_VTABLE(vaStatus, ctx, EndPicture);
CHECK_VTABLE(vaStatus, ctx, SyncSurface);
CHECK_VTABLE(vaStatus, ctx, QuerySurfaceStatus);
CHECK_VTABLE(vaStatus, ctx, QueryImageFormats);
CHECK_VTABLE(vaStatus, ctx, CreateImage);
CHECK_VTABLE(vaStatus, ctx, DeriveImage);
CHECK_VTABLE(vaStatus, ctx, DestroyImage);
CHECK_VTABLE(vaStatus, ctx, SetImagePalette);
CHECK_VTABLE(vaStatus, ctx, GetImage);
CHECK_VTABLE(vaStatus, ctx, PutImage);
CHECK_VTABLE(vaStatus, ctx, QuerySubpictureFormats);
CHECK_VTABLE(vaStatus, ctx, CreateSubpicture);
CHECK_VTABLE(vaStatus, ctx, DestroySubpicture);
CHECK_VTABLE(vaStatus, ctx, SetSubpictureImage);
CHECK_VTABLE(vaStatus, ctx, SetSubpictureChromakey);
CHECK_VTABLE(vaStatus, ctx, SetSubpictureGlobalAlpha);
CHECK_VTABLE(vaStatus, ctx, AssociateSubpicture);
CHECK_VTABLE(vaStatus, ctx, DeassociateSubpicture);
CHECK_VTABLE(vaStatus, ctx, QueryDisplayAttributes);
CHECK_VTABLE(vaStatus, ctx, GetDisplayAttributes);
CHECK_VTABLE(vaStatus, ctx, SetDisplayAttributes);
}
if (VA_STATUS_SUCCESS != vaStatus) {
va_errorMessage(dpy, "%s init failed\n", driver_path);
dlclose(handle);
}
if (VA_STATUS_SUCCESS == vaStatus)
ctx->handle = handle;
free(driver_path);
vaStatus = va_openDriverFromPath(dpy, driver_path);
free(driver_path);
if (VA_STATUS_SUCCESS == vaStatus)
break;
}
}
free(driver_path);

driver_dir = strtok_r(NULL, ENV_VAR_SEPARATOR, &saveptr);
}

Expand Down Expand Up @@ -715,6 +720,10 @@ static VAStatus va_new_opendriver(VADisplay dpy)
}

vaStatus = va_openDriver(dpy, drivers[i]);
/* Try as path as fallback */
if (vaStatus != VA_STATUS_SUCCESS)
sivileri marked this conversation as resolved.
Show resolved Hide resolved
vaStatus = va_openDriverFromPath(dpy, drivers[i]);

va_infoMessage(dpy, "va_openDriver() returns %d\n", vaStatus);

if (vaStatus == VA_STATUS_SUCCESS)
Expand Down
2 changes: 1 addition & 1 deletion va/win32/va_win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* 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";
const char VAAPI_DEFAULT_DRIVER_NAME[] = "vaon12_drv_video";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emmm. But "vaon12_drv_video" is not a full path. Full path (on Windows) would be for example "C:\msys64\mingw64\bin\vaon12_drv_video.dll". You probably can name it "full driver name", but saying "full path" I personally understand different thing.

Copy link
Contributor Author

@sivileri sivileri Jun 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the commit message is confusing, I'll reword it. The full path should be .\vaon12_drv_video.dll intended as default for the Nuget package that ships vaon12_drv_video.dll in the same directory as va.dll, va_win32.dll.

As in Win LoadLibrary (aliased to dlopen) supports the file string to have multiple semantics I only left the filename, which will first still try to open .\vaon12_drv_video.dll and then attempt again using the default Windows library search order.

  • If no file name extension is specified in the lpFileName parameter, the default library extension .dll is appended.
  • When no path is specified, the function searches for loaded modules whose base name matches the base name of the module to be loaded. If the name matches, the load succeeds. Otherwise, the function searches for the file. The first directory searched is the directory containing the image file used to create the calling process...


typedef struct _VADisplayContextWin32 {
LUID adapter_luid;
Expand Down