Skip to content
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
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,48 @@ cmake -G "NMake Makefiles" CMAKE_CXX_FLAGS="/EHsc" ..
nmake
```

# Configuring loader settings via the Windows registry

On Windows, every loader environment variable (for example `ZEL_ENABLE_LOADER_LOGGING`,
`ZEL_LOADER_LOG_CONSOLE`, `ZEL_LIBRARY_PATH`, `ZE_ENABLE_VALIDATION_LAYER`, ...) can also be
supplied through the registry. This is useful when the L0 application is launched as a child
process of a GUI application that cannot conveniently pass environment variables to its
subprocesses.

Values are read from the `Environment` subkey of the loader key:

```
HKEY_CURRENT_USER\Software\Intel\oneAPI\LevelZero\Environment
HKEY_LOCAL_MACHINE\Software\Intel\oneAPI\LevelZero\Environment
```

Each setting is a registry value whose **name matches the environment variable name** and whose
data is the value the variable would otherwise hold. Both `REG_SZ` (string) and `REG_DWORD`
(numeric) value types are supported — a `REG_DWORD` of `1` is equivalent to a `REG_SZ` of `"1"`.

Lookup order (first match wins):

1. The process environment variable (if set) — always takes precedence, so existing scripts and
CI setups are unaffected.
2. `HKEY_CURRENT_USER` (per-user, writable without administrator rights).
3. `HKEY_LOCAL_MACHINE` (machine-wide).

Example — enable console logging at `trace` level for the current user with a `.reg` file:

```
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Intel\oneAPI\LevelZero\Environment]
"ZEL_LOADER_LOG_CONSOLE"="1"
"ZEL_LOADER_LOGGING_LEVEL"="trace"
```

Or from the command line:

```sh
reg add "HKCU\Software\Intel\oneAPI\LevelZero\Environment" /v ZEL_LOADER_LOG_CONSOLE /t REG_SZ /d 1 /f
```


# Contributing

Expand Down
140 changes: 96 additions & 44 deletions source/inc/ze_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,93 @@ inline std::string readLevelZeroLoaderLibraryPath() {

return LoaderRegKeyPath;
}

// Reads a single Level Zero loader setting from the given registry root under
// the "Environment" subkey of the loader key. Value names match the
// corresponding environment variable names (e.g. "ZEL_LOADER_LOG_CONSOLE").
// Supports REG_SZ / REG_EXPAND_SZ (used verbatim) and REG_DWORD (rendered as a
// decimal string) so that numeric flags can be stored either way. Returns true
// and populates 'value' when the named value exists and is of a supported type.
inline bool readLoaderSettingFromRegistryRoot(HKEY root, const char* name, std::string& value) {
static constexpr char settingsKeyPath[] = "Software\\Intel\\oneAPI\\LevelZero\\Environment";

HKEY regKey = {};
if (ERROR_SUCCESS != RegOpenKeyExA(root, settingsKeyPath, 0, KEY_QUERY_VALUE, &regKey)) {
return false;
}

DWORD regValueType = {};
DWORD dataSize = {};
bool found = false;
LSTATUS regOpStatus = RegQueryValueExA(regKey, name, NULL, &regValueType, NULL, &dataSize);

if (ERROR_SUCCESS == regOpStatus) {
if ((REG_SZ == regValueType || REG_EXPAND_SZ == regValueType) && dataSize > 0) {
std::string buffer(dataSize, '\0');
regOpStatus = RegQueryValueExA(regKey, name, NULL, &regValueType,
(LPBYTE) & *buffer.begin(), &dataSize);
if (ERROR_SUCCESS == regOpStatus) {
// dataSize includes the terminating null for string types; trim
// at the first null so 'value' holds a clean C-string.
value.assign(buffer.c_str());
found = true;
}
} else if (REG_DWORD == regValueType && dataSize == sizeof(DWORD)) {
DWORD dwordValue = 0;
regOpStatus = RegQueryValueExA(regKey, name, NULL, &regValueType,
(LPBYTE)&dwordValue, &dataSize);
if (ERROR_SUCCESS == regOpStatus) {
value.assign(std::to_string(dwordValue));
found = true;
}
}
}

RegCloseKey(regKey);
return found;
}

// Fallback lookup for loader settings on Windows: checks the current user hive
// first (HKEY_CURRENT_USER, writable without administrator rights) and then the
// machine-wide hive (HKEY_LOCAL_MACHINE). This lets loader environment settings
// be configured persistently in the registry for processes (e.g. GUI apps and
// their child processes) that cannot conveniently inherit them from the shell.
inline bool readLoaderSettingFromRegistry(const char* name, std::string& value) {
if (readLoaderSettingFromRegistryRoot(HKEY_CURRENT_USER, name, value)) {
return true;
}
return readLoaderSettingFromRegistryRoot(HKEY_LOCAL_MACHINE, name, value);
}
#endif

// Looks up a loader setting by name. The process environment always takes
// precedence; on Windows, if the variable is not present in the environment the
// registry fallback (see readLoaderSettingFromRegistry) is consulted. Returns
// true and populates 'value' when a value is found from either source.
inline bool getenv_raw(const char* name, std::string& value) {
#if defined(_WIN32)
// Query the required buffer size (includes the terminating null); a return
// of 0 means the variable is not set in the environment.
DWORD required = GetEnvironmentVariableA(name, nullptr, 0);
if (0 != required) {
std::string buffer(required, '\0');
DWORD written = GetEnvironmentVariableA(name, &buffer[0], required);
if (0 != written && written < required) {
buffer.resize(written);
value.assign(std::move(buffer));
return true;
}
}
return readLoaderSettingFromRegistry(name, value);
#else
const char* env = getenv(name);
if (nullptr == env) {
return false;
}
value.assign(env);
return true;
#endif
}

//////////////////////////////////////////////////////////////////////////
#if !defined(_WIN32) && (__GNUC__ >= 4)
Expand All @@ -131,65 +217,31 @@ typedef struct _cl_program* cl_program;
///////////////////////////////////////////////////////////////////////////////
inline bool getenv_tobool( const char* name )
{
const char* env = nullptr;

#if defined(_WIN32)
char buffer[8];
auto rc = GetEnvironmentVariable(name, buffer, 8);
if (0 != rc && rc <= 8) {
env = buffer;
}
#else
env = getenv(name);
#endif

if( ( nullptr == env ) || ( 0 == strcmp( "0", env ) ) )
std::string value;
if( !getenv_raw( name, value ) || ( "0" == value ) )
return false;
return ( 0 == strcmp( "1", env ) );
return ( "1" == value );
}

// Returns the numeric mode (0, 1, or 2) for env vars that support a two-level
// flag scheme: 0 = off, 1 = legacy/normal, 2 = advanced/verbose.
// Any value other than "1" or "2" is treated as 0.
inline uint32_t getenv_tomode( const char* name )
{
const char* env = nullptr;

#if defined(_WIN32)
char buffer[8];
auto rc = GetEnvironmentVariable(name, buffer, 8);
if (0 != rc && rc <= 8) {
env = buffer;
}
#else
env = getenv(name);
#endif

if (env == nullptr || strcmp("0", env) == 0)
std::string value;
if (!getenv_raw(name, value) || value == "0")
return 0;
if (strcmp("2", env) == 0)
if (value == "2")
return 2;
if (strcmp("1", env) == 0)
if (value == "1")
return 1;
return 0;
}


inline std::string getenv_string ( const char* name){

const char* env = nullptr;

#if defined(_WIN32)
char buffer[1024];
auto rc = GetEnvironmentVariable(name, buffer, 1024);
if (0 != rc && rc <= 1024) {
env = buffer;
}
#else
env = getenv(name);
#endif

if ((nullptr == env))
std::string value;
if (!getenv_raw(name, value))
return "";
return std::string(env);
return value;
}
10 changes: 10 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ add_executable(
loader_tracing_layer.cpp
)

# The registry fallback for loader environment settings is a Windows-only feature.
if(WIN32)
target_sources(tests PRIVATE getenv_registry_unit_tests.cpp)
endif()

# Only include driver_ordering_unit_tests and driver_teardown_unit_tests for static builds or non-Windows platforms
# as they require internal loader symbols that are not exported in Windows DLLs
if(BUILD_STATIC OR NOT WIN32)
Expand Down Expand Up @@ -119,6 +124,11 @@ endif()
add_test(NAME tests_api COMMAND tests --gtest_filter=*GivenLevelZeroLoaderPresentWhenCallingzeGetLoaderVersionsAPIThenValidVersionIsReturned*)
set_property(TEST tests_api PROPERTY ENVIRONMENT "ZE_ENABLE_LOADER_DEBUG_TRACE=1;ZE_ENABLE_NULL_DRIVER=1")

# Windows-only: registry fallback for loader environment settings.
if(WIN32)
add_test(NAME tests_getenv_registry_fallback COMMAND tests --gtest_filter=*GetenvRegistryFallbackTest*)
endif()

# LoaderVersionAPI Tests
add_test(NAME tests_loader_version_after_init COMMAND tests --gtest_filter=*LoaderVersionAPI*GivenLoaderWhenCallingzelGetLoaderVersionAfterInitThenValidVersionIsReturned*)
set_property(TEST tests_loader_version_after_init PROPERTY ENVIRONMENT "ZE_ENABLE_LOADER_DEBUG_TRACE=1;ZE_ENABLE_NULL_DRIVER=1")
Expand Down