Skip to content

Sparse group IDs break group-root isolation #1612

Description

@shi-eric

wp.Bvh and wp.Mesh accept sparse, nonnegative group IDs at construction, but wp.bvh_get_group_root() and wp.mesh_get_group_root() do not reliably isolate the requested group.

With dense groups [0, 0, 1, 1], querying group 0 is correctly restricted to items 0 and 1. Changing only the second group ID to produce [0, 0, 2, 2] causes a group-0 query to include items from group 2.

This reproduces with both Bvh and Mesh on CPU and CUDA.

Expected behavior

Looking up group 0 should restrict traversal to the items assigned to group 0, regardless of whether another group is labeled 1 or 2.

For the mesh case below, a ray restricted to group 0 should not hit face 2, which belongs to group 2.

Actual behavior

A BVH query restricted to sparse group 0 returns items from both groups. A mesh ray restricted to sparse group 0 hits a face from group 2.

Reproduction

import warp as wp


@wp.kernel
def query_bvh_group(
    bvh_id: wp.uint64,
    group_id: int,
    hits: wp.array[wp.int32],
):
    root = wp.bvh_get_group_root(bvh_id, group_id)
    query = wp.bvh_query_aabb(
        bvh_id,
        wp.vec3(-10.0, -10.0, -10.0),
        wp.vec3(200.0, 10.0, 10.0),
        root,
    )

    item = int(0)
    while wp.bvh_query_next(query, item):
        hits[item] = 1


@wp.kernel
def query_mesh_group(
    mesh_id: wp.uint64,
    group_id: int,
    face_out: wp.array[wp.int32],
):
    root = wp.mesh_get_group_root(mesh_id, group_id)
    hit = wp.mesh_query_ray(
        mesh_id,
        wp.vec3(100.2, 0.2, -1.0),
        wp.vec3(0.0, 0.0, 1.0),
        1.0e6,
        root,
    )
    if hit.result:
        face_out[0] = hit.face


def run_bvh_case(device: str, group_ids: list[int]) -> list[int]:
    lowers = wp.array(
        [
            [0.0, 0.0, 0.0],
            [2.0, 0.0, 0.0],
            [100.0, 0.0, 0.0],
            [102.0, 0.0, 0.0],
        ],
        dtype=wp.vec3,
        device=device,
    )
    uppers = wp.array(
        [
            [1.0, 1.0, 1.0],
            [3.0, 1.0, 1.0],
            [101.0, 1.0, 1.0],
            [103.0, 1.0, 1.0],
        ],
        dtype=wp.vec3,
        device=device,
    )
    groups = wp.array(group_ids, dtype=wp.int32, device=device)
    bvh = wp.Bvh(lowers, uppers, groups=groups)

    hits = wp.zeros(4, dtype=wp.int32, device=device)
    wp.launch(
        query_bvh_group,
        dim=1,
        inputs=[bvh.id, 0],
        outputs=[hits],
        device=device,
    )
    return [index for index, value in enumerate(hits.numpy()) if value]


def run_mesh_case(device: str, group_ids: list[int]) -> int:
    points = wp.array(
        [
            [0.0, 0.0, 0.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [2.0, 0.0, 0.0],
            [3.0, 0.0, 0.0],
            [2.0, 1.0, 0.0],
            [100.0, 0.0, 0.0],
            [101.0, 0.0, 0.0],
            [100.0, 1.0, 0.0],
            [102.0, 0.0, 0.0],
            [103.0, 0.0, 0.0],
            [102.0, 1.0, 0.0],
        ],
        dtype=wp.vec3,
        device=device,
    )
    indices = wp.array(list(range(12)), dtype=wp.int32, device=device)
    groups = wp.array(group_ids, dtype=wp.int32, device=device)
    mesh = wp.Mesh(points, indices, groups=groups)

    face = wp.full(1, -1, dtype=wp.int32, device=device)
    wp.launch(
        query_mesh_group,
        dim=1,
        inputs=[mesh.id, 0],
        outputs=[face],
        device=device,
    )
    return int(face.numpy()[0])


devices = ["cpu"]
if wp.is_cuda_available():
    devices.append("cuda:0")

for device in devices:
    dense_groups = [0, 0, 1, 1]
    sparse_groups = [0, 0, 2, 2]

    print(f"{device} dense BVH hits:  {run_bvh_case(device, dense_groups)}")
    print(f"{device} sparse BVH hits: {run_bvh_case(device, sparse_groups)}")
    print(f"{device} dense mesh face:  {run_mesh_case(device, dense_groups)}")
    print(f"{device} sparse mesh face: {run_mesh_case(device, sparse_groups)}")

Output

cpu dense BVH hits:  [0, 1]
cpu sparse BVH hits: [0, 1, 2, 3]
cpu dense mesh face:  -1
cpu sparse mesh face: 2
cuda:0 dense BVH hits:  [0, 1]
cuda:0 sparse BVH hits: [0, 1, 2, 3]
cuda:0 dense mesh face:  -1
cuda:0 sparse mesh face: 2

System Information

  • Warp 1.15.0.dev0 at commit 59e6ba10c79b572856dd21ba17b520a97d9c3e30
  • Python 3.12.13
  • Linux 6.8.0, x86_64
  • CUDA Toolkit 13.0
  • CUDA Driver 13.0
  • Reproduces on both CPU and CUDA

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No fields configured for Bug.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions