Cycles: Show devices with outdated drivers as disabled in preferences

When a GPU device's driver does not meet Blender's minimum required
version, the device is now shown in the preferences as a greyed-out
entry with a message inside the brackets, indicating which driver
version is needed, instead of being silently hidden.

This helps users understand why their GPU is not available for
rendering and what action they can take to resolve the situation.

Pull Request: https://projects.blender.org/blender/blender/pulls/159405
This commit is contained in:
Nikita Sirgienko 2026-06-25 10:51:09 +02:00 • committed by Nikita Sirgienko
parent 0f29a66845
commit 4f07d79bcc
12 changed files with 219 additions and 41 deletions

View file

@ -1613,7 +1613,7 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
class CyclesDeviceSettings(bpy.types.PropertyGroup):
# Runtime properties
__slots__ = ("is_optimized")
__slots__ = ("is_optimized", "meets_driver_requirement")
# Properties saved in preferences
id: StringProperty(name="ID", description="Unique identifier of the device")
@ -1751,6 +1751,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
for device in device_list:
entry = self.find_existing_device_entry(device)
entry.is_optimized = device[7]
entry.meets_driver_requirement = device[8]
if entry.type == compute_device_type:
devices.append(entry)
elif entry.type == 'CPU':
@ -1789,6 +1790,11 @@ class CyclesPreferences(bpy.types.AddonPreferences):
for device in self.get_device_list(compute_device_type):
if device[1] != compute_device_type:
continue
# Skip devices that do not meet the driver requirement.
if not device[8]:
continue
for dev in self.devices:
if dev.use and dev.id == device[2]:
num += 1
@ -1800,6 +1806,11 @@ class CyclesPreferences(bpy.types.AddonPreferences):
for device in self.get_device_list(compute_device_type):
if device[1] == compute_device_type:
continue
# Skip devices that do not meet the driver requirement.
if not device[8]:
continue
for dev in self.devices:
if dev.use and dev.id == device[2]:
return True
@ -1819,6 +1830,10 @@ class CyclesPreferences(bpy.types.AddonPreferences):
if device_type == 'CPU':
continue
# Skip devices that do not meet the driver requirement.
if not device[8]:
continue
has_device_oidn_support = device[5]
if has_device_oidn_support and self.find_existing_device_entry(device).use:
return True
@ -1835,6 +1850,10 @@ class CyclesPreferences(bpy.types.AddonPreferences):
if device_type == 'CPU':
continue
# Skip devices that do not meet the driver requirement.
if not device[8]:
continue
has_device_optixdenoiser_support = device[6]
if has_device_optixdenoiser_support and self.find_existing_device_entry(device).use:
return True
@ -1861,6 +1880,14 @@ class CyclesPreferences(bpy.types.AddonPreferences):
found_device = True
break
optix_minimum_driver_version = "535"
hip_minimum_adrenalin_driver_version = "24.9.1"
hip_minimum_pro_driver_version = "24.Q4"
hip_minimum_linux_driver_version = "24.30"
hip_rocm_minimum_version = "6.3"
oneapi_minimum_windows_driver_version = "XX.X.101.8306"
oneapi_minimum_linux_driver_version = "XX.XX.37435.3"
if not found_device:
col = box.column(align=True)
col.label(text=rpt_("No compatible GPUs found for Cycles"), icon='INFO', translate=False)
@ -1871,36 +1898,31 @@ class CyclesPreferences(bpy.types.AddonPreferences):
icon='BLANK1', translate=False)
elif device_type == 'OPTIX':
compute_capability = "5.0"
driver_version = "535"
col.label(text=rpt_("Requires NVIDIA GPU with compute capability %s") % compute_capability,
icon='BLANK1', translate=False)
col.label(text=rpt_("and NVIDIA driver version %s or newer") % driver_version,
col.label(text=rpt_("and NVIDIA driver version %s or newer") % optix_minimum_driver_version,
icon='BLANK1', translate=False)
elif device_type == 'HIP':
import sys
if sys.platform[:3] == "win":
adrenalin_driver_version = "24.9.1"
pro_driver_version = "24.Q4"
col.label(
text=rpt_("Requires AMD GPU with RDNA architecture"),
icon='BLANK1',
translate=False)
col.label(text=rpt_("and AMD Adrenalin driver %s or newer") %
adrenalin_driver_version, icon='BLANK1', translate=False)
hip_minimum_adrenalin_driver_version, icon='BLANK1', translate=False)
col.label(text=rpt_("or AMD Radeon Pro %s driver or newer") %
pro_driver_version, icon='BLANK1', translate=False)
hip_minimum_pro_driver_version, icon='BLANK1', translate=False)
elif sys.platform.startswith("linux"):
rocm_version = "6.3"
driver_version = "24.30"
col.label(
text=rpt_("Requires AMD GPU with RDNA architecture"),
icon='BLANK1',
translate=False)
col.label(
text=rpt_("and ROCm HIP Runtime %s or newer") %
rocm_version, icon='BLANK1', translate=False)
hip_rocm_minimum_version, icon='BLANK1', translate=False)
col.label(text=rpt_("or AMD driver version %s or newer") %
driver_version, icon='BLANK1', translate=False)
hip_minimum_linux_driver_version, icon='BLANK1', translate=False)
elif device_type == 'ONEAPI':
import sys
if sys.platform.startswith("win"):
@ -1912,16 +1934,14 @@ class CyclesPreferences(bpy.types.AddonPreferences):
# and no intermediate versions were publicly available between 8250 and 8331 for Intel® Arc™ GPUs.
# As a result, we can safely recommend users to use driver version 8306 or higher, without needing
# to distinguish between Intel® Arc™ and Intel® Arc™ Pro users.
driver_version = "XX.X.101.8306"
col.label(
text=self._format_device_name(
rpt_("Requires Intel(R) Arc(TM) GPUs or newer Intel(R) Graphics")),
icon='BLANK1',
translate=False)
col.label(text=rpt_("with Windows driver version %s or newer") % driver_version,
icon='BLANK1', translate=False)
col.label(text=rpt_("with Windows driver version %s or newer") %
oneapi_minimum_windows_driver_version, icon='BLANK1', translate=False)
elif sys.platform.startswith("linux"):
driver_version = "XX.XX.37435.3"
col.label(
text=self._format_device_name(
rpt_("Requires Intel(R) Arc(TM) GPUs or newer Intel(R) Graphics")),
@ -1931,7 +1951,11 @@ class CyclesPreferences(bpy.types.AddonPreferences):
text=rpt_(" - intel-level-zero-gpu or intel-compute-runtime version"),
icon='BLANK1',
translate=False)
col.label(text=rpt_(" %s or newer") % driver_version, icon='BLANK1', translate=False)
col.label(
text=rpt_(" %s or newer") %
oneapi_minimum_linux_driver_version,
icon='BLANK1',
translate=False)
col.label(text=rpt_(" - oneAPI Level-Zero Loader"), icon='BLANK1', translate=False)
elif device_type == 'METAL':
mac_version = "12.2"
@ -1939,11 +1963,58 @@ class CyclesPreferences(bpy.types.AddonPreferences):
icon='BLANK1', translate=False)
return
has_usable_gpu_device = False
for device in devices:
name = self._format_device_name(device.name)
if not device.is_optimized:
name += rpt_(" (Unoptimized Performance)")
box.prop(device, "use", text=name, translate=False)
col = box.column()
row = col.row()
if not device.meets_driver_requirement:
import sys
row.active = False
name += rpt_(" (Disabled)")
row.prop(device, "use", text=name, translate=False)
details = ""
if device.type == 'OPTIX':
details = rpt_("Requires NVIDIA driver version %s or newer") % optix_minimum_driver_version
elif device.type == 'HIP':
if sys.platform[:3] == "win":
details = rpt_("Requires AMD Adrenalin driver %s or newer, or AMD Radeon Pro %s driver or newer") % (
hip_minimum_adrenalin_driver_version, hip_minimum_pro_driver_version)
elif sys.platform.startswith("linux"):
details = rpt_("Requires ROCm HIP Runtime %s or newer, or AMD driver version %s or newer") % (
hip_rocm_minimum_version, hip_minimum_linux_driver_version)
elif device.type == 'ONEAPI':
if sys.platform.startswith("win"):
details = rpt_(
"Requires Windows driver version %s or newer") % oneapi_minimum_windows_driver_version
elif sys.platform.startswith("linux"):
details = rpt_(
"Requires intel-level-zero-gpu or intel-compute-runtime version %s or newer") % oneapi_minimum_linux_driver_version
if not details:
details = rpt_("Driver upgrade required")
sub = col.row()
sub.active = False
sub.label(icon='BLANK1', text=details, translate=False)
else:
if device.type != 'CPU':
if device.use:
has_usable_gpu_device = True
else:
# CPU is always listed last (by convention in get_devices()),
# see get_devices_for_type implementation.
# Grey it out if no GPU device is enabled, because CPU here
# is meant as a supplement to GPU rendering. For CPU-only
# rendering, select the "None" compute device type instead.
if not has_usable_gpu_device:
row.active = False
row.prop(device, "use", text=name, translate=False)
def draw_impl(self, layout, context):
row = layout.row()
@ -1966,8 +2037,18 @@ class CyclesPreferences(bpy.types.AddonPreferences):
if device[1] != compute_device_type:
continue
# device[8] == DeviceInfo.meets_driver_requirement
# For more details see available_devices_func function in python.cpp
if not device[8]:
# Devices that do not meet the driver requirement are not used;
# skip them.
continue
# device[3] == DeviceInfo.has_peer_memory
if device[3]:
has_peer_memory = True
# device[4] == DeviceInfo.use_hardware_raytracing
if device[4]:
has_enabled_hardware_rt = True
else:

View file

@ -142,7 +142,11 @@ DeviceInfo blender_device_info(blender::UserDef &b_preferences,
const string id = get_string(device, "id");
for (const DeviceInfo &info : devices) {
if (info.id == id) {
used_devices.push_back(info);
/* Devices not meeting driver requirements are still enumerated for UI reporting,
* but must not be added to the active render device list. */
if (info.meets_driver_requirement) {
used_devices.push_back(info);
}
break;
}
}
@ -164,7 +168,16 @@ DeviceInfo blender_device_info(blender::UserDef &b_preferences,
DeviceInfo device;
if (BlenderSession::device_override != DEVICE_MASK_ALL) {
const vector<DeviceInfo> devices = Device::available_devices(BlenderSession::device_override);
const vector<DeviceInfo> available_devices = Device::available_devices(
BlenderSession::device_override);
vector<DeviceInfo> devices;
for (const DeviceInfo &info : available_devices) {
/* Devices not meeting driver requirements are still enumerated for UI reporting,
* but must not be added to the active render device list. */
if (info.meets_driver_requirement) {
devices.push_back(info);
}
}
if (devices.empty()) {
device = Device::dummy_device("Found no Cycles device of the specified type");

View file

@ -429,7 +429,7 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
for (size_t i = 0; i < devices.size(); i++) {
const DeviceInfo &device = devices[i];
const string type_name = Device::string_from_type(device.type);
PyObject *device_tuple = PyTuple_New(8);
PyObject *device_tuple = PyTuple_New(9);
PyTuple_SET_ITEM(device_tuple, 0, pyunicode_from_string(device.description.c_str()));
PyTuple_SET_ITEM(device_tuple, 1, pyunicode_from_string(type_name.c_str()));
PyTuple_SET_ITEM(device_tuple, 2, pyunicode_from_string(device.id.c_str()));
@ -439,6 +439,7 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
device_tuple, 5, PyBool_FromLong(device.denoisers & DENOISER_OPENIMAGEDENOISE));
PyTuple_SET_ITEM(device_tuple, 6, PyBool_FromLong(device.denoisers & DENOISER_OPTIX));
PyTuple_SET_ITEM(device_tuple, 7, PyBool_FromLong(device.has_execution_optimization));
PyTuple_SET_ITEM(device_tuple, 8, PyBool_FromLong(device.meets_driver_requirement));
PyTuple_SET_ITEM(ret, i, device_tuple);
}

View file

@ -276,8 +276,17 @@ vector<DeviceInfo> Device::available_devices(const uint mask)
#ifdef WITH_OPTIX
if (mask & DEVICE_MASK_OPTIX) {
if (!(devices_initialized_mask & DEVICE_MASK_OPTIX)) {
if (device_optix_init()) {
bool meets_nvidia_driver_requirement = true;
if (device_optix_init(&meets_nvidia_driver_requirement) || !meets_nvidia_driver_requirement)
{
device_optix_info(cuda_devices(), optix_devices());
for (DeviceInfo &info : optix_devices()) {
info.meets_driver_requirement = meets_nvidia_driver_requirement;
}
}
else {
/* `device_optix_init` has failed but not because of the driver being too old.
* Nothing to do in this case. */
}
devices_initialized_mask |= DEVICE_MASK_OPTIX;
}
@ -290,8 +299,29 @@ vector<DeviceInfo> Device::available_devices(const uint mask)
#ifdef WITH_HIP
if (mask & DEVICE_MASK_HIP) {
if (!(devices_initialized_mask & DEVICE_MASK_HIP)) {
if (device_hip_init()) {
bool meets_amd_driver_requirement = true;
if (device_hip_init(&meets_amd_driver_requirement)) {
device_hip_info(hip_devices());
for (DeviceInfo &info : hip_devices()) {
info.meets_driver_requirement = meets_amd_driver_requirement;
}
}
else if (meets_amd_driver_requirement == false) {
/* If we are here, then hipewInit has failed with HIPEW_ERROR_OLD_DRIVER. */
/* It is unclear if proper device info can be collected at this point, so we create
* a placeholder device to communicate the need to upgrade the driver, as presumably
* the hardware is available. */
DeviceInfo info = DeviceInfo();
info.type = DEVICE_HIP;
info.description = "Unknown AMD device";
info.id = "unknown_amd_device_with_outdated_driver";
info.num = 0;
info.meets_driver_requirement = false;
hip_devices().push_back(info);
}
else {
/* `device_hip_init` has failed but not because of the driver being too old.
* Nothing to do in this case. */
}
devices_initialized_mask |= DEVICE_MASK_HIP;
}

View file

@ -96,6 +96,14 @@ class DeviceInfo {
/* Indicate that device execution has been optimized by Blender or vendor developers.
* For LTS versions, this helps communicate that newer versions may have better performance. */
bool has_execution_optimization = true;
/* True if device's driver is above the minimal Blender required version, false otherwise.
* Needed for properly communicating this fact back to the user, who then can choose to upgrade
* the driver or do nothing.
*
* Default value is chosen to be true intentionally - assume compliant unless proven otherwise,
* especially since CPU devices do not have any minimal versions, as well as some GPU backends,
* for example CUDA. */
bool meets_driver_requirement = true;
KernelOptimizationLevel kernel_optimization_level =
KERNEL_OPTIMIZATION_LEVEL_FULL; /* Optimization level applied to path tracing

View file

@ -24,8 +24,11 @@
CCL_NAMESPACE_BEGIN
bool device_hip_init()
bool device_hip_init(bool *r_meets_driver_requirement)
{
if (r_meets_driver_requirement) {
*r_meets_driver_requirement = true;
}
#if !defined(WITH_HIP)
return false;
#elif defined(WITH_HIP_DYNLOAD)
@ -43,6 +46,13 @@ bool device_hip_init()
LOG_INFO << "HIPEW initialization succeeded";
if (!hipSupportsDriver()) {
LOG_WARNING << "Driver version is too old";
if (r_meets_driver_requirement) {
*r_meets_driver_requirement = false;
}
/* The device will not be used because it does not meet the driver requirement.
* Returning true ensures the real AMD device information is still reported
* in the UI. */
result = true;
}
else if (HIPDevice::have_precompiled_kernels()) {
LOG_INFO << "Found precompiled kernels";
@ -64,6 +74,9 @@ bool device_hip_init()
else if (hipew_result == HIPEW_ERROR_OLD_DRIVER) {
LOG_WARNING << "HIPEW initialization failed: Driver version too old, requires AMD Adrenalin "
"driver 24.9.1 or newer, or AMD Radeon Pro driver 24.Q4 or newer";
if (r_meets_driver_requirement) {
*r_meets_driver_requirement = false;
}
}
else {
LOG_WARNING << "HIPEW initialization failed: Error opening HIP dynamic library";

View file

@ -15,7 +15,7 @@ class DeviceInfo;
class Profiler;
class Stats;
bool device_hip_init();
bool device_hip_init(bool *r_meets_driver_requirement = nullptr);
unique_ptr<Device> device_hip_create(const DeviceInfo &info,
Stats &stats,

View file

@ -105,6 +105,7 @@ static void device_iterator_cb(const char *id,
bool hwrt_support,
bool oidn_support,
bool has_execution_optimization,
bool meets_driver_requirement,
void *user_ptr)
{
vector<DeviceInfo> *devices = (vector<DeviceInfo> *)user_ptr;
@ -147,6 +148,7 @@ static void device_iterator_cb(const char *id,
# 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 << "\".";

View file

@ -38,7 +38,12 @@ extern "C" void rtcSetDeviceSYCLDevice(RTCDevice device, const sycl::device sycl
CCL_NAMESPACE_BEGIN
static std::vector<sycl::device> available_sycl_devices(
struct SyclDeviceEntry {
sycl::device device;
bool meets_driver_requirement;
};
static std::vector<SyclDeviceEntry> available_sycl_devices(
bool *multiple_level_zero_platforms_detected);
static int parse_driver_build_version(const sycl::device &device);
@ -1051,13 +1056,21 @@ bool OneapiDevice::create_queue(SyclQueue *&external_queue,
*multiple_level_zero_platforms_detected_pointer = false;
try {
std::vector<sycl::device> devices = available_sycl_devices(
std::vector<SyclDeviceEntry> devices = available_sycl_devices(
multiple_level_zero_platforms_detected_pointer);
if (device_index < 0 || device_index >= devices.size()) {
return false;
}
sycl::queue *created_queue = new sycl::queue(devices[device_index],
if (devices[device_index].meets_driver_requirement == false) {
oneapi_error_string_ = "The device driver is too old.";
LOG_ERROR << "Internal error: The SYCL device does not meet minimum driver requirement, but "
"it was used anyway. Please report a bug.";
return false;
}
sycl::queue *created_queue = new sycl::queue(devices[device_index].device,
sycl::property::queue::in_order());
external_queue = reinterpret_cast<SyclQueue *>(created_queue);
@ -1072,7 +1085,7 @@ bool OneapiDevice::create_queue(SyclQueue *&external_queue,
"\"intel-level-zero-gpu-raytracing\" to enable it or disable Embree on GPU.";
}
else {
rtcSetDeviceSYCLDevice(*device_object_ptr, devices[device_index]);
rtcSetDeviceSYCLDevice(*device_object_ptr, devices[device_index].device);
}
}
# else
@ -1439,10 +1452,10 @@ int parse_driver_build_version(const sycl::device &device)
return driver_build_version;
}
std::vector<sycl::device> available_sycl_devices(
std::vector<SyclDeviceEntry> available_sycl_devices(
bool *multiple_level_zero_platforms_detected = nullptr)
{
std::vector<sycl::device> available_devices;
std::vector<SyclDeviceEntry> available_devices;
bool allow_all_devices = false;
if (getenv("CYCLES_ONEAPI_ALL_DEVICES") != nullptr) {
allow_all_devices = true;
@ -1470,6 +1483,7 @@ std::vector<sycl::device> available_sycl_devices(
for (const sycl::device &device : oneapi_devices) {
bool filter_out = false;
bool meets_driver_requirement = true;
if (!allow_all_devices) {
/* For now we support all Intel(R) Arc(TM) devices and likely any future GPU,
@ -1520,7 +1534,7 @@ std::vector<sycl::device> available_sycl_devices(
lowest_supported_driver_version_win :
lowest_supported_driver_version_neo;
if (driver_build_version < lowest_supported_driver_version) {
filter_out = true;
meets_driver_requirement = false;
LOG_WARNING << "Driver version for device \""
<< device.get_info<sycl::info::device::name>()
@ -1539,8 +1553,8 @@ std::vector<sycl::device> available_sycl_devices(
/* The order of adding devices is not important, as both duplicated GPUs are fully
* functional and performant, so we can pick up the first one we find. */
if (!filter_out) {
for (const sycl::device &already_available_device : available_devices) {
std::array<sycl::device, 2> devices = {already_available_device, device};
for (const SyclDeviceEntry &available_entry : available_devices) {
std::array<sycl::device, 2> devices = {available_entry.device, device};
std::vector<sycl::ext::intel::info::device::uuid::return_type> uuids;
for (int i = 0; i < 2; i++) {
/* As this is an Intel-specific enumeration issue - we are collecting Intel UUID
@ -1577,7 +1591,7 @@ std::vector<sycl::device> available_sycl_devices(
}
if (!filter_out) {
available_devices.push_back(device);
available_devices.push_back(SyclDeviceEntry{device, meets_driver_requirement});
}
}
}
@ -1670,8 +1684,10 @@ char *OneapiDevice::device_capabilities()
{
std::stringstream capabilities;
const std::vector<sycl::device> &oneapi_devices = available_sycl_devices();
for (const sycl::device &device : oneapi_devices) {
const std::vector<SyclDeviceEntry> &entries = available_sycl_devices();
for (const SyclDeviceEntry &entry : entries) {
const sycl::device &device = entry.device;
const std::string &name = device.get_info<sycl::info::device::name>();
capabilities << std::string("\t") << name << "\n";
@ -1686,6 +1702,8 @@ char *OneapiDevice::device_capabilities()
capabilities << arch_name << "\n";
capabilities << "\t\tsycl::info::device::is_cycles_optimized\t\t\t";
capabilities << is_optimised_for_arch << "\n";
capabilities << "\t\tsycl::info::device::meets_driver_requirement\t\t\t";
capabilities << entry.meets_driver_requirement << "\n";
# define WRITE_ATTR(attribute_name, attribute_variable) \
capabilities << "\t\tsycl::info::device::" #attribute_name "\t\t\t" << attribute_variable \
@ -1785,8 +1803,10 @@ char *OneapiDevice::device_capabilities()
void OneapiDevice::iterate_devices(OneAPIDeviceIteratorCallback cb, void *user_ptr)
{
int num = 0;
std::vector<sycl::device> devices = available_sycl_devices();
for (sycl::device &device : devices) {
std::vector<SyclDeviceEntry> entries = available_sycl_devices();
for (const SyclDeviceEntry &entry : entries) {
const sycl::device &device = entry.device;
const std::string &platform_name =
device.get_platform().get_info<sycl::info::platform::name>();
std::string name = device.get_info<sycl::info::device::name>();
@ -1816,6 +1836,7 @@ void OneapiDevice::iterate_devices(OneAPIDeviceIteratorCallback cb, void *user_p
hwrt_support,
oidn_support,
is_optimised_for_arch,
entry.meets_driver_requirement,
user_ptr);
num++;
}

View file

@ -16,7 +16,7 @@ CCL_NAMESPACE_BEGIN
class DeviceQueue;
using OneAPIDeviceIteratorCallback =
void (*)(const char *, const char *, const int, bool, bool, bool, void *);
void (*)(const char *, const char *, const int, bool, bool, bool, bool, void *);
class OneapiDevice : public GPUDevice {
private:

View file

@ -28,8 +28,11 @@
CCL_NAMESPACE_BEGIN
bool device_optix_init()
bool device_optix_init(bool *r_meets_driver_requirement)
{
if (r_meets_driver_requirement) {
*r_meets_driver_requirement = true;
}
#ifdef WITH_OPTIX
if (OPTIX_FUNCTION_TABLE_SYMBOL.optixDeviceContextCreate != nullptr) {
/* Already initialized function table. */
@ -46,6 +49,9 @@ bool device_optix_init()
if (result == OPTIX_ERROR_UNSUPPORTED_ABI_VERSION) {
LOG_WARNING << "OptiX initialization failed because the installed NVIDIA driver is too old. "
"Please update to the latest driver first!";
if (r_meets_driver_requirement) {
*r_meets_driver_requirement = false;
}
return false;
}
if (result != OPTIX_SUCCESS) {
@ -93,6 +99,7 @@ void device_optix_info(const vector<DeviceInfo> &cuda_devices, vector<DeviceInfo
}
# endif
info.meets_driver_requirement = true;
devices.push_back(info);
}
#else

View file

@ -14,13 +14,15 @@ class DeviceInfo;
class Profiler;
class Stats;
bool device_optix_init();
bool device_optix_init(bool *r_meets_driver_requirement = nullptr);
unique_ptr<Device> device_optix_create(const DeviceInfo &info,
Stats &stats,
Profiler &profiler,
bool headless);
/** Generate proper OptiX DeviceInfo based on an existing CUDA DeviceInfo for the same device.
* Does not require usage of the OptiX API, only CUDA API. */
void device_optix_info(const vector<DeviceInfo> &cuda_devices, vector<DeviceInfo> &devices);
CCL_NAMESPACE_END