Skip to content

Commit

Permalink
Do not include CUDA devices in raycast tests.
Browse files Browse the repository at this point in the history
pybind some SYCL util functions.
  • Loading branch information
ssheorey committed Nov 19, 2024
1 parent 239ccf8 commit 9ef538a
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 87 deletions.
5 changes: 5 additions & 0 deletions cpp/open3d/core/SYCLUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,16 @@ std::vector<Device> GetAvailableSYCLDevices() {
}

void enablePersistentJITCache() {
#ifdef BUILD_SYCL_MODULE
#if defined(_WIN32)
_putenv_s("SYCL_CACHE_PERSISTENT", "1");
#else
setenv("SYCL_CACHE_PERSISTENT", "1", 1);
#endif
#endif
utility::LogInfo(
"enablePersistentJITCache is not compiled with "
"BUILD_SYCL_MODULE=ON.");
}

} // namespace sy
Expand Down
3 changes: 2 additions & 1 deletion cpp/open3d/core/SYCLUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ bool IsDeviceAvailable(const Device& device);
/// Return a list of available SYCL devices.
std::vector<Device> GetAvailableSYCLDevices();

/// Enables the JIT cache for SYCL.
/// Enables the JIT cache for SYCL. This sets an environment variable and will
/// affect the entire process and any child processes.
void enablePersistentJITCache();

} // namespace sy
Expand Down
3 changes: 2 additions & 1 deletion cpp/open3d/t/geometry/RaycastingScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class RaycastingScene {
/// \brief Add a triangle mesh to the scene.
/// \param vertex_positions Vertices as Tensor of dim {N,3} and dtype float.
/// \param triangle_indices Triangles as Tensor of dim {M,3} and dtype
/// uint32_t. \return The geometry ID of the added mesh.
/// uint32_t.
/// \return The geometry ID of the added mesh.
uint32_t AddTriangles(const core::Tensor &vertex_positions,
const core::Tensor &triangle_indices);

Expand Down
8 changes: 8 additions & 0 deletions cpp/pybind/core/sycl_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ void pybind_sycl_utils_definitions(py::module& m) {
m_sycl.def("is_available", sy::IsAvailable,
"Returns true if Open3D is compiled with SYCL support and at "
"least one compatible SYCL device is detected.");

m_sycl.def("get_available_devices", sy::GetAvailableSYCLDevices,
"Return a list of available SYCL devices.");

m_sycl.def("enable_persistent_jit_cache", sy::enablePersistentJITCache,
"Enables the JIT cache for SYCL. This sets an environment "
"variable and "
"will affect the entire process and any child processes.");
}

} // namespace core
Expand Down
37 changes: 12 additions & 25 deletions python/test/open3d_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
# SPDX-License-Identifier: MIT
# ----------------------------------------------------------------------------

import os
import sys
import urllib.request
import zipfile

import numpy as np
import pytest


def torch_available():
try:
Expand All @@ -23,26 +15,21 @@ def torch_available():
return True


def list_devices(enable_sycl=False):
def list_devices(enable_cuda=True, enable_sycl=False):
"""
If Open3D is built with CUDA support:
- If cuda device is available, returns [Device("CPU:0"), Device("CUDA:0")].
- If cuda device is not available, returns [Device("CPU:0")].
If Open3D is built with SYCL support:
- If SYCL device is available, returns [Device("CPU:0"), Device("SYCL:0")].
- If SYCL device is not available, returns [Device("CPU:0")].
If Open3D is built without CUDA support:
- returns [Device("CPU:0")].
Returns a list of devices that are available for Open3D to use:
- Device("CPU:0")
- Device("CUDA:0") if built with CUDA support and a CUDA device is available.
- Device("SYCL:0") if built with SYCL support and a SYCL device is available.
"""
import open3d as o3d
if o3d.core.cuda.device_count() > 0:
return [o3d.core.Device("CPU:0"), o3d.core.Device("CUDA:0")]
elif enable_sycl and o3d.core.sycl.is_available():
return [o3d.core.Device("CPU:0"), o3d.core.Device("SYCL:0")]
else:
return [o3d.core.Device("CPU:0")]

devices = [o3d.core.Device("CPU:0")]
if enable_cuda and o3d.core.cuda.device_count() > 0:
devices.append(o3d.core.Device("CUDA:0"))
if enable_sycl and o3d.core.sycl.is_available():
return devices.append(o3d.core.Device("SYCL:0"))
return devices


def list_devices_with_torch():
Expand Down
139 changes: 79 additions & 60 deletions python/test/t/geometry/test_raycasting_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@

import sys
import os

sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../..")
from open3d_test import list_devices


# test intersection with a single triangle
@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_cast_rays(device):
vertices = o3d.core.Tensor([[0, 0, 0], [1, 0, 0], [1, 1, 0]],
dtype=o3d.core.float32,
Expand All @@ -28,24 +30,27 @@ def test_cast_rays(device):
scene = o3d.t.geometry.RaycastingScene(device=device)
geom_id = scene.add_triangles(vertices, triangles)

rays = o3d.core.Tensor([[0.2, 0.1, 1, 0, 0, -1], [10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device)
rays = o3d.core.Tensor(
[[0.2, 0.1, 1, 0, 0, -1], [10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device,
)
ans = scene.cast_rays(rays)

# first ray hits the triangle
assert geom_id == ans['geometry_ids'][0].cpu()
assert np.isclose(ans['t_hit'][0].item(), 1.0)
assert geom_id == ans["geometry_ids"][0].cpu()
assert np.isclose(ans["t_hit"][0].item(), 1.0)

# second ray misses
assert o3d.t.geometry.RaycastingScene.INVALID_ID == ans['geometry_ids'][
assert o3d.t.geometry.RaycastingScene.INVALID_ID == ans["geometry_ids"][
1].cpu()
assert np.isinf(ans['t_hit'][1].item())
assert np.isinf(ans["t_hit"][1].item())


# cast lots of random rays to test the internal batching
# we expect no errors for this test
@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_cast_lots_of_rays(device):
vertices = o3d.core.Tensor([[0, 0, 0], [1, 0, 0], [1, 1, 0]],
dtype=o3d.core.float32,
Expand All @@ -65,7 +70,8 @@ def test_cast_lots_of_rays(device):


# test occlusion with a single triangle
@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_test_occlusions(device):
vertices = o3d.core.Tensor([[0, 0, 0], [1, 0, 0], [1, 1, 0]],
dtype=o3d.core.float32,
Expand All @@ -77,9 +83,11 @@ def test_test_occlusions(device):
scene = o3d.t.geometry.RaycastingScene(device=device)
scene.add_triangles(vertices, triangles)

rays = o3d.core.Tensor([[0.2, 0.1, 1, 0, 0, -1], [10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device)
rays = o3d.core.Tensor(
[[0.2, 0.1, 1, 0, 0, -1], [10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device,
)
ans = scene.test_occlusions(rays).cpu()

# first ray is occluded by the triangle
Expand All @@ -99,7 +107,8 @@ def test_test_occlusions(device):

# test lots of random rays for occlusions to test the internal batching
# we expect no errors for this test
@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_test_lots_of_occlusions(device):
vertices = o3d.core.Tensor([[0, 0, 0], [1, 0, 0], [1, 1, 0]],
dtype=o3d.core.float32,
Expand All @@ -118,24 +127,28 @@ def test_test_lots_of_occlusions(device):
_ = scene.test_occlusions(rays)


@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_add_triangle_mesh(device):
cube = o3d.t.geometry.TriangleMesh.create_box()
cube = cube.to(device)

scene = o3d.t.geometry.RaycastingScene(device=device)
scene.add_triangles(cube)

rays = o3d.core.Tensor([[0.5, 0.5, -1, 0, 0, 1], [0.5, 0.5, 0.5, 0, 0, 1],
[10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device)
rays = o3d.core.Tensor(
[[0.5, 0.5, -1, 0, 0, 1], [0.5, 0.5, 0.5, 0, 0, 1],
[10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device,
)
ans = scene.count_intersections(rays)

np.testing.assert_equal(ans.cpu().numpy(), [2, 1, 0])


@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_count_intersections(device):
cube = o3d.t.geometry.TriangleMesh.create_box()
vertex_positions = cube.vertex.positions
Expand All @@ -147,18 +160,21 @@ def test_count_intersections(device):
scene = o3d.t.geometry.RaycastingScene(device=device)
scene.add_triangles(vertex_positions, triangle_indices)

rays = o3d.core.Tensor([[0.5, 0.5, -1, 0, 0, 1], [0.5, 0.5, 0.5, 0, 0, 1],
[10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device)
rays = o3d.core.Tensor(
[[0.5, 0.5, -1, 0, 0, 1], [0.5, 0.5, 0.5, 0, 0, 1],
[10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device,
)
ans = scene.count_intersections(rays)

np.testing.assert_equal(ans.cpu().numpy(), [2, 1, 0])


# count lots of random ray intersections to test the internal batching
# we expect no errors for this test
@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_count_lots_of_intersections(device):
cube = o3d.t.geometry.TriangleMesh.create_box()
vertex_positions = cube.vertex.positions
Expand All @@ -177,7 +193,8 @@ def test_count_lots_of_intersections(device):
_ = scene.count_intersections(rays)


@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_list_intersections(device):
cube = o3d.t.geometry.TriangleMesh.create_box()
vertex_positions = cube.vertex.positions
Expand All @@ -189,21 +206,24 @@ def test_list_intersections(device):
scene = o3d.t.geometry.RaycastingScene(device=device)
scene.add_triangles(vertex_positions, triangle_indices)

rays = o3d.core.Tensor([[0.5, 0.5, -1, 0, 0, 1], [0.5, 0.5, 0.5, 0, 0, 1],
[10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device)
rays = o3d.core.Tensor(
[[0.5, 0.5, -1, 0, 0, 1], [0.5, 0.5, 0.5, 0, 0, 1],
[10, 10, 10, 1, 0, 0]],
dtype=o3d.core.float32,
device=device,
)
ans = scene.list_intersections(rays)

np.testing.assert_allclose(ans['t_hit'].cpu().numpy(),
np.testing.assert_allclose(ans["t_hit"].cpu().numpy(),
np.array([1.0, 2.0, 0.5]),
rtol=1e-6,
atol=1e-6)


# list lots of random ray intersections to test the internal batching
# we expect no errors for this test
@pytest.mark.parametrize("device", list_devices(enable_sycl=True))
@pytest.mark.parametrize("device",
list_devices(enable_cuda=False, enable_sycl=True))
def test_list_lots_of_intersections(device):
cube = o3d.t.geometry.TriangleMesh.create_box()
vertex_positions = cube.vertex.positions
Expand Down Expand Up @@ -234,12 +254,14 @@ def test_compute_closest_points():
dtype=o3d.core.float32)
ans = scene.compute_closest_points(query_points)

assert (geom_id == ans['geometry_ids']).all()
assert (0 == ans['primitive_ids']).all()
np.testing.assert_allclose(ans['points'].numpy(),
np.array([[0.2, 0.1, 0.0], [1, 1, 0]]),
rtol=1e-6,
atol=1e-6)
assert (geom_id == ans["geometry_ids"]).all()
assert (0 == ans["primitive_ids"]).all()
np.testing.assert_allclose(
ans["points"].numpy(),
np.array([[0.2, 0.1, 0.0], [1, 1, 0]]),
rtol=1e-6,
atol=1e-6,
)


# compute lots of closest points to test the internal batching
Expand Down Expand Up @@ -325,45 +347,42 @@ def test_output_shapes(shape):

# some outputs append a specific last dim
last_dim = {
't_hit': [],
'geometry_ids': [],
'primitive_ids': [],
'primitive_uvs': [2],
'primitive_normals': [3],
'points': [3],
'ray_ids': [],
'ray_splits': []
"t_hit": [],
"geometry_ids": [],
"primitive_ids": [],
"primitive_uvs": [2],
"primitive_normals": [3],
"points": [3],
"ray_ids": [],
"ray_splits": [],
}

ans = scene.cast_rays(rays)
for k, v in ans.items():
expected_shape = shape + last_dim[k]
assert list(
v.shape
) == expected_shape, 'shape mismatch: expected {} but got {} for {}'.format(
expected_shape, list(v.shape), k)
assert (list(v.shape) == expected_shape
), "shape mismatch: expected {} but got {} for {}".format(
expected_shape, list(v.shape), k)

ans = scene.compute_closest_points(query_points)
for k, v in ans.items():
expected_shape = shape + last_dim[k]
assert list(
v.shape
) == expected_shape, 'shape mismatch: expected {} but got {} for {}'.format(
expected_shape, list(v.shape), k)
assert (list(v.shape) == expected_shape
), "shape mismatch: expected {} but got {} for {}".format(
expected_shape, list(v.shape), k)

ans = scene.list_intersections(rays)
nx = np.sum(scene.count_intersections(rays).numpy()).tolist()
for k, v in ans.items():
if k == 'ray_splits':
if k == "ray_splits":
alt_shape = [np.prod(rays.shape[:-1]) + 1]
else:
alt_shape = [nx]
#use np.append otherwise issues if alt_shape = [0] and last_dim[k] = []
# use np.append otherwise issues if alt_shape = [0] and last_dim[k] = []
expected_shape = np.append(alt_shape, last_dim[k]).tolist()
assert list(
v.shape
) == expected_shape, 'shape mismatch: expected {} but got {} for {}'.format(
expected_shape, list(v.shape), k)
assert (list(v.shape) == expected_shape
), "shape mismatch: expected {} but got {} for {}".format(
expected_shape, list(v.shape), k)


def test_sphere_wrong_occupancy():
Expand Down

0 comments on commit 9ef538a

Please sign in to comment.