From d28f514f7ec5df710d22ce288ffed642ef57ed93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Gorschl=C3=BCter?= Date: Fri, 20 Dec 2024 12:46:35 +0100 Subject: [PATCH] Fix python package-DLLs not being found This PR introduced `winmode=0` for loading DLLs during import: https://github.com/isl-org/Open3D/pull/5259 This however leads to directories added via `os.add_dll_directory` not being considered, when loading DLLs (see https://github.com/isl-org/Open3D/issues/7104#issuecomment-2556557535). In particular this leads to open3d/tbb12.dll not being found. Fix this by going back to the default DLL-search mode (`winmode=None`). In order to still be able to find CUDA-DLLs, that are not part of the open3d-package, search their directory in PATH and explicitly add it to the DLL-search-locations for CUDA-builds. --- CHANGELOG.md | 1 + python/open3d/__init__.py | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad1f4f43b17..2653187a98c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## Main +- Fix DLLs not being found in Python-package. Also prevent PATH from being searched for DLLs, except CUDA (PR #7108) - Fix MSAA sample count not being copied when FilamentView is copied - Fix TriangleMesh::SamplePointsUniformly and TriangleMesh::SamplePointsPoissonDisk now sampling colors from mesh if available (PR #6842) - Fix TriangleMesh::SamplePointsUniformly not sampling triangle meshes uniformly (PR #6653) diff --git a/python/open3d/__init__.py b/python/open3d/__init__.py index d4d5c613b0e..e5bd61900d3 100644 --- a/python/open3d/__init__.py +++ b/python/open3d/__init__.py @@ -15,6 +15,7 @@ # https://github.com/dmlc/xgboost/issues/1715 import os import sys +import re os.environ["KMP_DUPLICATE_LIB_OK"] = "True" # Enable thread composability manager to coordinate Intel OpenMP and TBB threads. Only works with Intel OpenMP. @@ -27,21 +28,6 @@ from open3d._build_config import _build_config -def load_cdll(path): - """ - Wrapper around ctypes.CDLL to take care of Windows compatibility. - """ - path = Path(path) - if not path.is_file(): - raise FileNotFoundError(f"Shared library file not found: {path}.") - - if sys.platform == "win32" and sys.version_info >= (3, 8): - # https://stackoverflow.com/a/64472088/1255535 - return CDLL(str(path), winmode=0) - else: - return CDLL(str(path)) - - if sys.platform == "win32": # Unix: Use rpath to find libraries _win32_dll_dir = os.add_dll_directory(str(Path(__file__).parent)) @@ -50,7 +36,7 @@ def load_cdll(path): # Load CPU pybind dll gracefully without introducing new python variable. # Do this before loading the CUDA pybind dll to correctly resolve symbols try: # StopIteration if cpu version not available - load_cdll(str(next((Path(__file__).parent / "cpu").glob("pybind*")))) + CDLL(str(next((Path(__file__).parent / "cpu").glob("pybind*")))) except StopIteration: warnings.warn( "Open3D was built with CUDA support, but Open3D CPU Python " @@ -59,9 +45,23 @@ def load_cdll(path): ImportWarning, ) try: + if sys.platform == "win32" and sys.version_info >= (3, 8): + # Since Python 3.8, the PATH environment variable is not used to find DLLs anymore. + # To allow Windows users to use Open3D with CUDA without running into dependency-problems, + # look for the CUDA bin directory in PATH and explicitly add it to the DLL search path. + cuda_bin_path = None + for path in os.environ['PATH'].split(';'): + # search heuristic: look for a path containing "cuda" and "bin" in this order. + if re.search(r'cuda.*bin', path, re.IGNORECASE): + cuda_bin_path = path + break + + if cuda_bin_path: + os.add_dll_directory(cuda_bin_path) + # Check CUDA availability without importing CUDA pybind symbols to # prevent "symbol already registered" errors if first import fails. - _pybind_cuda = load_cdll( + _pybind_cuda = CDLL( str(next((Path(__file__).parent / "cuda").glob("pybind*")))) if _pybind_cuda.open3d_core_cuda_device_count() > 0: from open3d.cuda.pybind import ( @@ -212,4 +212,4 @@ def _jupyter_nbextension_paths(): if sys.platform == "win32": _win32_dll_dir.close() -del os, sys, CDLL, load_cdll, find_library, Path, warnings, _insert_pybind_names +del os, sys, CDLL, find_library, Path, warnings, _insert_pybind_names