blender/intern/cycles/device/oneapi/device.cpp
Christoph Neuhauser 6d864c8f0a Fix #159584: Cycles, Compositor: Do not bundle UR Level Zero v1 adapter
A bug that existed in Level Zero loader versions up to 1.28.2 (fixed by
https://github.com/oneapi-src/level-zero/pull/435). It causes a crash
when calling `zeInitDrivers` a second time when no Level Zero drivers
are available. In Cycles and the Compositor module, this behavior is
triggered when `sycl::platform::get_platforms()` is called (in the case
of the Compositor module transitively via OIDN). SYCL loads both the
Level Zero v1 and v2 adapters, which both call the problematic function.

As a workaround, the SYCL UR Level Zero v1 adapter library files are
no longer bundled and the `SYCL_UR_USE_LEVEL_ZERO_V2=1` environment
variable is set to force devices to use the v2 adapter that would
otherwise default to v1. The v2 adapter has been shipped by Blender
starting with 5.0 (using DPC++ version 6.2), but has been available
in DPC++ since version 6.0.

Pull Request: https://projects.blender.org/blender/blender/pulls/163087
2026-08-26 11:22:17 +02:00

191 lines
5.6 KiB
C++

/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "device/oneapi/device.h"
#include "device/device.h"
#include "util/log.h"
#ifdef WITH_ONEAPI
# include "device/oneapi/device_impl.h"
# include "integrator/denoiser_oidn_gpu.h" // IWYU pragma: keep
# include "util/string.h"
# ifdef __linux__
# include <dlfcn.h>
# endif
#endif /* WITH_ONEAPI */
CCL_NAMESPACE_BEGIN
bool device_oneapi_init()
{
#if !defined(WITH_ONEAPI)
return false;
#else
/* NOTE(@nsirgien): we need to enable JIT cache from here and
* right now this cache policy is controlled by env. variables. */
/* NOTE(@xavierh-intel) we enable the use of copy engine, incl. for fill
* operations as it lowers the overhead from zeCommandListAppendMemoryFill
* when running paths_array kernels on Linux+A750.
* All these env variable can be set beforehand by end-users and
* will in that case -not- be overwritten. */
/* By default, enable only Level-Zero and if all devices are allowed, also CUDA and HIP.
* OpenCL backend isn't currently well supported. */
# ifdef _WIN32
if (getenv("SYCL_CACHE_PERSISTENT") == nullptr) {
_putenv_s("SYCL_CACHE_PERSISTENT", "1");
}
if (getenv("SYCL_CACHE_THRESHOLD") == nullptr) {
_putenv_s("SYCL_CACHE_THRESHOLD", "0");
}
if (getenv("ONEAPI_DEVICE_SELECTOR") == nullptr) {
if (getenv("CYCLES_ONEAPI_ALL_DEVICES") == nullptr) {
_putenv_s("ONEAPI_DEVICE_SELECTOR", "level_zero:*");
}
else {
_putenv_s("ONEAPI_DEVICE_SELECTOR", "!opencl:*");
}
}
/* SYSMAN is needed for free_memory queries. */
if (getenv("ZES_ENABLE_SYSMAN") == nullptr) {
_putenv_s("ZES_ENABLE_SYSMAN", "1");
}
if (getenv("UR_L0_USE_COPY_ENGINE") == nullptr) {
_putenv_s("UR_L0_USE_COPY_ENGINE", "1");
}
if (getenv("UR_L0_USE_COPY_ENGINE_FOR_FILL") == nullptr) {
_putenv_s("UR_L0_USE_COPY_ENGINE_FOR_FILL", "1");
}
/* As a fix for #163040, the v1 Level Zero adapter is not included. */
if (getenv("SYCL_UR_USE_LEVEL_ZERO_V2") == nullptr) {
_putenv_s("SYCL_UR_USE_LEVEL_ZERO_V2", "1");
}
# elif __linux__
setenv("SYCL_CACHE_PERSISTENT", "1", false);
setenv("SYCL_CACHE_THRESHOLD", "0", false);
if (getenv("CYCLES_ONEAPI_ALL_DEVICES") == nullptr) {
setenv("ONEAPI_DEVICE_SELECTOR", "level_zero:*", false);
}
else {
setenv("ONEAPI_DEVICE_SELECTOR", "!opencl:*", false);
}
/* SYSMAN is needed for free_memory queries. */
setenv("ZES_ENABLE_SYSMAN", "1", false);
setenv("UR_L0_USE_COPY_ENGINE", "1", false);
setenv("UR_L0_USE_COPY_ENGINE_FOR_FILL", "1", false);
/* As a fix for #159584, the v1 Level Zero adapter is not included. */
setenv("SYCL_UR_USE_LEVEL_ZERO_V2", "1", false);
# endif
return true;
#endif
}
unique_ptr<Device> device_oneapi_create(const DeviceInfo &info,
Stats &stats,
Profiler &profiler,
bool headless)
{
#ifdef WITH_ONEAPI
return make_unique<OneapiDevice>(info, stats, profiler, headless);
#else
(void)info;
(void)stats;
(void)profiler;
(void)headless;
LOG_FATAL << "Requested to create oneAPI device while not enabled for this build.";
return nullptr;
#endif
}
#ifdef WITH_ONEAPI
static void device_iterator_cb(const char *id,
const char *name,
const int num,
bool hwrt_support,
bool oidn_support,
bool has_execution_optimization,
bool meets_driver_requirement,
void *user_ptr)
{
vector<DeviceInfo> *devices = (vector<DeviceInfo> *)user_ptr;
DeviceInfo info;
info.type = DEVICE_ONEAPI;
info.description = name;
info.num = num;
/* NOTE(@nsirgien): Should be unique at least on proper oneapi installation. */
info.id = id;
info.has_nanovdb = true;
# if defined(WITH_OPENIMAGEDENOISE)
# if OIDN_VERSION >= 20300
if (oidn_support) {
# else
if (OIDNDenoiserGPU::is_device_supported(info)) {
# endif
info.denoisers |= DENOISER_OPENIMAGEDENOISE;
}
# endif
(void)oidn_support;
info.has_gpu_queue = true;
/* NOTE(@nsirgien): oneAPI right now is focused on one device usage. In future it maybe will
* change, but right now peer access from one device to another device is not supported. */
info.has_peer_memory = false;
/* NOTE(@nsirgien): Seems not possible to know from SYCL/oneAPI or Level0. */
info.display_device = false;
# ifdef WITH_EMBREE_GPU
info.use_hardware_raytracing = hwrt_support;
# else
info.use_hardware_raytracing = false;
(void)hwrt_support;
# endif
info.has_execution_optimization = has_execution_optimization;
info.meets_driver_requirement = meets_driver_requirement;
devices->push_back(info);
LOG_INFO << "Added device \"" << info.description << "\" with id \"" << info.id << "\".";
if (info.denoisers & DENOISER_OPENIMAGEDENOISE) {
LOG_INFO << "Device with id \"" << info.id << "\" supports "
<< denoiserTypeToHumanReadable(DENOISER_OPENIMAGEDENOISE) << ".";
}
}
#endif
void device_oneapi_info(vector<DeviceInfo> &devices)
{
#ifdef WITH_ONEAPI
OneapiDevice::iterate_devices(device_iterator_cb, &devices);
#else /* WITH_ONEAPI */
(void)devices;
#endif /* WITH_ONEAPI */
}
string device_oneapi_capabilities()
{
string capabilities;
#ifdef WITH_ONEAPI
char *c_capabilities = OneapiDevice::device_capabilities();
if (c_capabilities) {
capabilities = c_capabilities;
free(c_capabilities);
}
#endif
return capabilities;
}
CCL_NAMESPACE_END