diff --git a/CHANGELOG.md b/CHANGELOG.md index 557bdaa5c..73426dca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## [1.3.3] - 2024-09-04 + +- Bug fixes + - Fix an aliasing issue with zero-copy array initialization from NumPy introduced in Warp 1.3.0. + - Fix `wp.Volume.load_from_numpy()` behavior when `bg_value` is a sequence of values. + ## [1.3.2] - 2024-08-30 - Bug fixes @@ -11,7 +17,6 @@ will no longer be unloaded before the graph is released. - Fix a bug in `wp.sim.collide.triangle_closest_point_barycentric()` where the returned barycentric coordinates may be incorrect when the closest point lies on an edge. - - Fix an aliasing issue with zero-copy array initialization from NumPy introduced in Warp 1.3.0. - Fix 32-bit overflow when array shape is specified using `np.int32`. - Fix handling of integer indices in the `input_output_mask` argument to `autograd.jacobian` and `autograd.jacobian_fd` ([GH-289](https://github.com/NVIDIA/warp/issues/289)). diff --git a/README.md b/README.md index 4fc215db5..2e4780125 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ the `pip install` command, e.g. | Platform | Install Command | | --------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| Linux aarch64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.2/warp_lang-1.3.2+cu11-py3-none-manylinux2014_aarch64.whl` | -| Linux x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.2/warp_lang-1.3.2+cu11-py3-none-manylinux2014_x86_64.whl` | -| Windows x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.2/warp_lang-1.3.2+cu11-py3-none-win_amd64.whl` | +| Linux aarch64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.3/warp_lang-1.3.3+cu11-py3-none-manylinux2014_aarch64.whl` | +| Linux x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.3/warp_lang-1.3.3+cu11-py3-none-manylinux2014_x86_64.whl` | +| Windows x86-64 | `pip install https://github.com/NVIDIA/warp/releases/download/v1.3.3/warp_lang-1.3.3+cu11-py3-none-win_amd64.whl` | The `--force-reinstall` option may need to be used to overwrite a previous installation. diff --git a/VERSION.md b/VERSION.md index 1892b9267..31e5c8434 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.3.2 +1.3.3 diff --git a/exts/omni.warp.core/config/extension.toml b/exts/omni.warp.core/config/extension.toml index 97f1ead7f..8b80e91f4 100644 --- a/exts/omni.warp.core/config/extension.toml +++ b/exts/omni.warp.core/config/extension.toml @@ -1,6 +1,6 @@ [package] # Semantic Versioning is used: https://semver.org/ -version = "1.3.2" +version = "1.3.3" authors = ["NVIDIA"] title = "Warp Core" description="The core Warp Python module" diff --git a/exts/omni.warp.core/docs/CHANGELOG.md b/exts/omni.warp.core/docs/CHANGELOG.md index 557bdaa5c..cb8f87da5 100644 --- a/exts/omni.warp.core/docs/CHANGELOG.md +++ b/exts/omni.warp.core/docs/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## [1.3.3] - 2024-09-04 + +- Bug fixes + - Fix an aliasing issue with zero-copy array initialization from NumPy introduced in Warp 1.3.0. + - Fix `wp.Volume.load_from_numpy()` behavior when `bg_value` is a sequence of values. + ## [1.3.2] - 2024-08-30 - Bug fixes diff --git a/exts/omni.warp/config/extension.toml b/exts/omni.warp/config/extension.toml index e13efa463..8e80c45d2 100644 --- a/exts/omni.warp/config/extension.toml +++ b/exts/omni.warp/config/extension.toml @@ -1,6 +1,6 @@ [package] # Semantic Versioning is used: https://semver.org/ -version = "1.3.2" +version = "1.3.3" authors = ["NVIDIA"] title = "Warp" description="Warp OmniGraph Nodes and Sample Scenes" @@ -35,7 +35,7 @@ exclude = ["Ogn*Database.py", "*/ogn*"] "omni.timeline" = {} "omni.ui" = {optional = true} "omni.usd" = {} -"omni.warp.core" = {version = "1.3.2", exact = true} +"omni.warp.core" = {version = "1.3.3", exact = true} [[python.module]] name = "omni.warp._extension" diff --git a/exts/omni.warp/docs/CHANGELOG.md b/exts/omni.warp/docs/CHANGELOG.md index 557bdaa5c..cb8f87da5 100644 --- a/exts/omni.warp/docs/CHANGELOG.md +++ b/exts/omni.warp/docs/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## [1.3.3] - 2024-09-04 + +- Bug fixes + - Fix an aliasing issue with zero-copy array initialization from NumPy introduced in Warp 1.3.0. + - Fix `wp.Volume.load_from_numpy()` behavior when `bg_value` is a sequence of values. + ## [1.3.2] - 2024-08-30 - Bug fixes diff --git a/tools/packman/bootstrap/configure.bat b/tools/packman/bootstrap/configure.bat index 33fb44138..8887b1c45 100755 --- a/tools/packman/bootstrap/configure.bat +++ b/tools/packman/bootstrap/configure.bat @@ -12,7 +12,7 @@ :: See the License for the specific language governing permissions and :: limitations under the License. -set PM_PACKMAN_VERSION=7.23.1 +set PM_PACKMAN_VERSION=7.24.1 :: Specify where packman command is rooted set PM_INSTALL_PATH=%~dp0.. diff --git a/tools/packman/bootstrap/install_package.py b/tools/packman/bootstrap/install_package.py index 4542c4bef..8735a0ec8 100644 --- a/tools/packman/bootstrap/install_package.py +++ b/tools/packman/bootstrap/install_package.py @@ -142,7 +142,7 @@ def generate_sha256_for_file(file_path: Union[str, os.PathLike]) -> str: def install_common_module(package_path, install_path): - COMMON_SHA256 = "0a2064434cca0170411c86f23349f9618556dc380d3589a2361db38ffeea9cac" + COMMON_SHA256 = "ce46783c5a938082514e796a014f8cca5870d6466c6a7147c35e230911dff143" package_sha256 = generate_sha256_for_file(package_path) if package_sha256 != COMMON_SHA256: raise RuntimeError( diff --git a/tools/packman/packman b/tools/packman/packman index b222d307a..03d297d35 100755 --- a/tools/packman/packman +++ b/tools/packman/packman @@ -24,7 +24,7 @@ else PM_CURL_SILENT="-s -S" PM_WGET_QUIET="--quiet" fi -export PM_PACKMAN_VERSION=7.23.1 +export PM_PACKMAN_VERSION=7.24.1 # This is necessary for newer macOS if [ `uname` == 'Darwin' ]; then @@ -60,17 +60,49 @@ if [ ! -d "$PM_PACKAGES_ROOT" ]; then mkdir -p -m a+rwx "$PM_PACKAGES_ROOT" fi +execute_with_retry() +{ + # Don't exit on error, we need to handle them + set +e + + local CMD="$1" + local MAX_TRIES=4 + local DELAY=2 + local TRIES=0 + local exit_code + + while [ $TRIES -lt $MAX_TRIES ] + do + ((TRIES++)) + eval $CMD + exit_code=$? + if [ $exit_code -eq 0 ]; then + return 0 + fi + + if [ $TRIES -lt $MAX_TRIES ]; then + echo "Attempt $TRIES failed. Retrying in $DELAY seconds ..." + sleep $DELAY + DELAY=$((DELAY * DELAY)) + echo "Retrying ..." + fi + done + + echo "Command failed after $MAX_TRIES attempts: $CMD" + return $exit_code +} + fetch_file_from_s3() { - SOURCE=$1 - SOURCE_URL=http://bootstrap.packman.nvidia.com/$SOURCE - TARGET=$2 + local SOURCE=$1 + local SOURCE_URL=http://bootstrap.packman.nvidia.com/$SOURCE + local TARGET=$2 echo "Fetching $SOURCE from bootstrap.packman.nvidia.com ..." + local CMD="curl -o $TARGET $SOURCE_URL $PM_CURL_SILENT" if command -v wget >/dev/null 2>&1; then - wget $PM_WGET_QUIET -O$TARGET $SOURCE_URL - else - curl -o $TARGET $SOURCE_URL $PM_CURL_SILENT + CMD="wget $PM_WGET_QUIET -O$TARGET $SOURCE_URL" fi + execute_with_retry "$CMD" } generate_temp_file_name() diff --git a/tools/packman/packmanconf.py b/tools/packman/packmanconf.py index db220d27e..f2dca9e3b 100644 --- a/tools/packman/packmanconf.py +++ b/tools/packman/packmanconf.py @@ -98,7 +98,13 @@ def get_module_dir(conf_dir, packages_root: str, version: str) -> str: tf = tempfile.NamedTemporaryFile(delete=False) target_name = tf.name tf.close() - url = f"http://bootstrap.packman.nvidia.com/packman-common@{version}.zip" + # Using http here and not https is by design. Unfortunately SSL keeps getting revised + # which breaks old clients when servers are forced to upgrade to newer version of TLS + # and refuse to downgrade when asked. Instead of relying on SSL for transport security + # packman does SHA256 verification of the downloaded package in the `install_package` + # method. We therefore inform SonarQube to stop complaining about the line below. + # See issue #367 for more detail. + url = f"http://bootstrap.packman.nvidia.com/packman-common@{version}.zip" # NOSONAR print(f"Downloading '{url}' ...") import urllib.request diff --git a/warp/config.py b/warp/config.py index b09c59472..e732f71b5 100644 --- a/warp/config.py +++ b/warp/config.py @@ -7,7 +7,7 @@ from typing import Optional -version: str = "1.3.2" +version: str = "1.3.3" """Warp version string""" verify_fp: bool = False diff --git a/warp/tests/test_array.py b/warp/tests/test_array.py index 6d115ebb2..5f853aba9 100644 --- a/warp/tests/test_array.py +++ b/warp/tests/test_array.py @@ -2295,6 +2295,39 @@ def test_array_from_numpy(test, device): assert_np_equal(result.numpy(), expected.numpy()) +def test_array_aliasing_from_numpy(test, device): + device = wp.get_device(device) + assert device.is_cpu + + a_np = np.ones(8, dtype=np.int32) + a_wp = wp.array(a_np, dtype=int, copy=False, device=device) + test.assertIs(a_wp._ref, a_np) # check that some ref is kept to original array + test.assertEqual(a_wp.ptr, a_np.ctypes.data) + + a_np_2 = a_wp.numpy() + test.assertTrue((a_np_2 == 1).all()) + + # updating source array should update aliased array + a_np.fill(2) + test.assertTrue((a_np_2 == 2).all()) + + # trying to alias from a different type should do a copy + # do it twice to check that the copy buffer is not being reused for different arrays + + b_np = np.ones(8, dtype=np.int64) + c_np = np.zeros(8, dtype=np.int64) + b_wp = wp.array(b_np, dtype=int, copy=False, device=device) + c_wp = wp.array(c_np, dtype=int, copy=False, device=device) + + test.assertNotEqual(b_wp.ptr, b_np.ctypes.data) + test.assertNotEqual(b_wp.ptr, c_wp.ptr) + + b_np_2 = b_wp.numpy() + c_np_2 = c_wp.numpy() + test.assertTrue((b_np_2 == 1).all()) + test.assertTrue((c_np_2 == 0).all()) + + def test_array_from_cai(test, device): import torch @@ -2447,6 +2480,7 @@ def test_array_new_del(self): add_function_test(TestArray, "test_array_of_structs_from_numpy", test_array_of_structs_from_numpy, devices=devices) add_function_test(TestArray, "test_array_of_structs_roundtrip", test_array_of_structs_roundtrip, devices=devices) add_function_test(TestArray, "test_array_from_numpy", test_array_from_numpy, devices=devices) +add_function_test(TestArray, "test_array_aliasing_from_numpy", test_array_aliasing_from_numpy, devices=["cpu"]) add_function_test(TestArray, "test_direct_from_numpy", test_direct_from_numpy, devices=["cpu"]) add_function_test(TestArray, "test_kernel_array_from_ptr", test_kernel_array_from_ptr, devices=devices) diff --git a/warp/tests/test_volume.py b/warp/tests/test_volume.py index 2fb1151b1..c227e38f0 100644 --- a/warp/tests/test_volume.py +++ b/warp/tests/test_volume.py @@ -843,6 +843,33 @@ def test_volume_from_numpy(test, device): test.assertIsNone(sphere_vdb_array.deleter) +def test_volume_from_numpy_3d(test, device): + # Volume.allocate_from_tiles() is only available with CUDA + mins = np.array([-3.0, -3.0, -3.0]) + voxel_size = 0.2 + maxs = np.array([3.0, 3.0, 3.0]) + nums = np.ceil((maxs - mins) / (voxel_size)).astype(dtype=int) + centers = np.array([[-1.0, -1.0, -1.0], [0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]) + rad = 2.5 + sphere_sdf_np = np.zeros(tuple(nums) + (3,)) + for x in range(nums[0]): + for y in range(nums[1]): + for z in range(nums[2]): + for k in range(3): + pos = mins + voxel_size * np.array([x, y, z]) + dis = np.linalg.norm(pos - centers[k]) + sphere_sdf_np[x, y, z, k] = dis - rad + sphere_vdb = wp.Volume.load_from_numpy( + sphere_sdf_np, mins, voxel_size, (rad + 3.0 * voxel_size,) * 3, device=device + ) + + test.assertNotEqual(sphere_vdb.id, 0) + + sphere_vdb_array = sphere_vdb.array() + test.assertEqual(sphere_vdb_array.dtype, wp.uint8) + test.assertIsNone(sphere_vdb_array.deleter) + + def test_volume_aniso_transform(test, device): # XY-rotation + z scale transform = [ @@ -894,6 +921,9 @@ def test_volume_new_del(self): add_function_test( TestVolume, "test_volume_from_numpy", test_volume_from_numpy, devices=get_selected_cuda_test_devices() ) +add_function_test( + TestVolume, "test_volume_from_numpy_3d", test_volume_from_numpy_3d, devices=get_selected_cuda_test_devices() +) add_function_test( TestVolume, "test_volume_aniso_transform", test_volume_aniso_transform, devices=get_selected_cuda_test_devices() ) diff --git a/warp/types.py b/warp/types.py index 4b648dfff..54022a30a 100644 --- a/warp/types.py +++ b/warp/types.py @@ -1601,6 +1601,9 @@ def __init__( self._array_interface = None self.is_transposed = False + # reference to other array + self._ref = None + # canonicalize dtype if dtype == int: dtype = int32 @@ -1652,9 +1655,6 @@ def __init__( if requires_grad: self._alloc_grad() - # reference to other array - self._ref = None - def _init_from_data(self, data, dtype, shape, device, copy, pinned): if not hasattr(data, "__len__"): raise RuntimeError(f"Data must be a sequence or array, got scalar {data}") @@ -2991,7 +2991,7 @@ def __init__(self, points=None, indices=None, velocities=None, support_winding_n Args: points (:class:`warp.array`): Array of vertex positions of type :class:`warp.vec3` - indices (:class:`warp.array`): Array of triangle indices of type :class:`warp.int32`, should be a 1d array with shape (num_tris, 3) + indices (:class:`warp.array`): Array of triangle indices of type :class:`warp.int32`, should be a 1d array with shape (num_tris * 3) velocities (:class:`warp.array`): Array of vertex velocities of type :class:`warp.vec3` (optional) support_winding_number (bool): If true the mesh will build additional datastructures to support `wp.mesh_query_point_sign_winding_number()` queries """ @@ -3529,8 +3529,9 @@ def load_from_numpy( ) if hasattr(bg_value, "__len__"): # vec3, assuming the numpy array is 4D - padded_array = np.array((target_shape[0], target_shape[1], target_shape[2], 3), dtype=np.single) - padded_array[:, :, :, :] = np.array(bg_value) + padded_array = np.full( + shape=(target_shape[0], target_shape[1], target_shape[2], 3), fill_value=bg_value, dtype=np.single + ) padded_array[0 : ndarray.shape[0], 0 : ndarray.shape[1], 0 : ndarray.shape[2], :] = ndarray else: padded_amount = (