From d1edf1d03e0da6868e1512cbc600289d809381d5 Mon Sep 17 00:00:00 2001 From: snowii <116462572+Snowii3D@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:42:54 -0400 Subject: [PATCH] Refactor onnxruntime dependency handling and importsFix onnxruntime compatibility for Blender 4.x/5.x on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The addon was broken on Blender 4.x+ for two reasons: 1. `onnxruntime==1.15.1` had no Python 3.11/3.12 wheel, causing a `ModuleNotFoundError` on import. 2. Upgrading directly to the latest onnxruntime (1.19+) caused a `DLL initialization routine failed` crash on Windows because those versions require the VS2022 C++ runtime, which is not bundled with Blender 4.x. ## Fix **`__init__.py`** - onnxruntime version is now selected dynamically based on the running Python version: - Python 3.11 / 3.12 (Blender 4.x) → `onnxruntime==1.18.1` (last version using the VS2019 runtime, compatible with Blender's bundled environment) - Python 3.13+ (Blender 5.x) → `onnxruntime==1.21.0` (minimum version with a Python 3.13 wheel) - Dependencies path is inserted at the **front** of `sys.path` so Blender's own paths can't shadow the installed package. - On Windows, both `os.add_dll_directory()` and `PATH` are updated to cover all Blender builds. - All stale `sys.modules` entries for the package and its submodules (e.g. `onnxruntime.capi`, `onnxruntime.capi._pybind_state`) are cleared before each import attempt, preventing a failed startup import from blocking later install-time imports. - `bl_info` minimum Blender version corrected back to `(4, 3, 2)`. ## Tested on - Blender 4.5 LTS, Windows 11 --- __init__.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/__init__.py b/__init__.py index 2c9d936..f8837dc 100644 --- a/__init__.py +++ b/__init__.py @@ -41,16 +41,47 @@ def get_dependencies_path(): # Python dependencies management helpers from : # https://github.com/robertguetzkow/blender-python-examples/tree/master/add_ons/install_dependencies Dependency = namedtuple('Dependency', ['module', 'package', 'name']) -dependencies = (Dependency(module='onnxruntime', package='onnxruntime==1.15.1', name='ort'), + +# onnxruntime 1.19+ requires the VS2022 C++ runtime (vcredist 2022). +# onnxruntime 1.18.x uses the VS2019 runtime which is far more common. +# Python 3.13 (Blender 5.x) requires at least 1.19, so we pick the minimum +# version that covers the running Python while staying on the older runtime +# wherever possible. +def _ort_package(): + if sys.version_info >= (3, 13): + return 'onnxruntime==1.21.0' # Python 3.13+ (Blender 5.x) + return 'onnxruntime==1.18.1' # Python 3.11/3.12 (Blender 4.x) + +dependencies = (Dependency(module='onnxruntime', package=_ort_package(), name='ort'), Dependency(module='numpy', package=None, name='np')) dependencies_installed = False def import_module(module_name, global_name=None, reload=True): - # Add addon dependencies path + # Add addon dependencies path at the FRONT so it takes priority deps_path = get_dependencies_path() - if deps_path not in sys.path : - sys.path.append(get_dependencies_path()) + if deps_path not in sys.path: + sys.path.insert(0, deps_path) + # On Windows (Python 3.8+), native DLLs in package subdirectories are no + # longer found automatically. Use both os.add_dll_directory (Python 3.8+) + # and PATH manipulation (legacy fallback) to cover all Blender builds. + if sys.platform == 'win32': + dll_dirs = [deps_path, os.path.join(deps_path, 'onnxruntime', 'capi')] + for dll_dir in dll_dirs: + if os.path.isdir(dll_dir): + if hasattr(os, 'add_dll_directory'): + os.add_dll_directory(dll_dir) + env_path = os.environ.get('PATH', '') + if dll_dir not in env_path: + os.environ['PATH'] = dll_dir + os.pathsep + env_path + # Clear ALL stale/failed imports for this module (including submodules like + # onnxruntime.capi, onnxruntime.capi._pybind_state) so a clean attempt is + # made after dependencies are installed. + if global_name not in globals(): + stale = [k for k in list(sys.modules) + if k == module_name or k.startswith(module_name + '.')] + for k in stale: + del sys.modules[k] # Import module if global_name is None: global_name = module_name