Skip to content

Commit 76458d2

Browse files
authored
Implement iterator over 2D pixel coordinates (#28)
Closes MET-56 ## Summary of Changes This PR implements a `PixelCoordRange` class, which defines `.begin()` and `.end()` methods compliant with C++'s range-expression. Instead of using two `Iterator` to denote the range, the `end()` returns a special `Sentinel` object, which requires less bookkeeping information (e.g. it doesn't need to keep another copy of the pixel start & end location). [You can read more about C++20's Iterator Sentinel feature here if interested :)](https://www.foonathan.net/2020/03/iterator-sentinel/). With this new class, we can now iterating over 2D pixels using range-based for loop ([as we wished for in Slack](https://probcomp.slack.com/archives/C09RM9VCCLQ/p1764124142124389)), i.e., ```cpp auto pixel_ranges = PixelCoordRange{ i_start, i_end, j_start, j_end }; for (auto pixel_coords : pixel_ranges) // do something ``` ## Test Plan This new pixel coordinate generator replaces the need to have a dedicated `Intrinsics.get_ray_directions_kernels` method (which is removed in this PR). As such, `camera_test.cu` has been updated accordingly to show & test that [range-based for loop](https://github.com/probcomp/GenMetaBalls/pull/28/files#diff-b9bfc85cd301b1b50504120cdc05a855e0cd3013c0d5d8d506821520b988a393R22-R26) works as expected. As always, to run the tests: ```bash pixi run ctest ```
1 parent b4d4d7f commit 76458d2

5 files changed

Lines changed: 74 additions & 20 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ set(CMAKE_CXX_STANDARD 20)
99
# Generate compile_commands.json for clang-tidy and other tools
1010
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
1111

12+
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
13+
1214
################
1315
# Core Library #
1416
################

genmetaballs/src/cuda/core/camera.cu

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <cstdint>
22
#include <cuda/std/ranges>
3+
#include <cuda/std/utility>
34
#include <cuda_runtime.h>
45

56
#include "camera.cuh"
@@ -11,3 +12,28 @@ CUDA_CALLABLE Vec3D Intrinsics::get_ray_direction(uint32_t px, uint32_t py) cons
1112
auto y = (static_cast<float>(py) - cy) / fy;
1213
return Vec3D{x, y, -1.0f};
1314
}
15+
16+
CUDA_CALLABLE cuda::std::pair<uint32_t, uint32_t> PixelCoordRange::Iterator::operator*() const {
17+
return cuda::std::make_pair(px, py);
18+
}
19+
20+
CUDA_CALLABLE PixelCoordRange::Iterator& PixelCoordRange::Iterator::operator++() {
21+
++px; // move to the next column
22+
if (px >= px_end) { // move to the next row
23+
px = px_start;
24+
++py;
25+
}
26+
return *this;
27+
}
28+
29+
CUDA_CALLABLE bool PixelCoordRange::Sentinel::operator==(const Iterator& it) const {
30+
return it.py >= py_end;
31+
}
32+
33+
CUDA_CALLABLE PixelCoordRange::Iterator PixelCoordRange::begin() const {
34+
return Iterator{px_start, px_end, py_start, px_start, py_start};
35+
}
36+
37+
CUDA_CALLABLE PixelCoordRange::Sentinel PixelCoordRange::end() const {
38+
return Sentinel{py_end};
39+
}
Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <cstdint>
4+
#include <cuda/std/utility>
45
#include <cuda_runtime.h>
56

67
#include "geometry.cuh"
@@ -17,20 +18,41 @@ struct Intrinsics {
1718
// Returns the direction of the ray going through pixel (px, py) in camera frame.
1819
// For efficiency, this function does not check if the pixel is within bounds.
1920
CUDA_CALLABLE Vec3D get_ray_direction(uint32_t px, uint32_t py) const;
21+
};
22+
23+
struct PixelCoordRange {
24+
uint32_t px_start;
25+
uint32_t px_end;
26+
uint32_t py_start;
27+
uint32_t py_end;
28+
29+
// the Iterator class holds the current pixel coordinates
30+
struct Iterator {
31+
// pixel range
32+
uint32_t px_start;
33+
uint32_t px_end;
34+
uint32_t py_start;
35+
36+
// current pixel coordinates
37+
uint32_t px;
38+
uint32_t py;
39+
40+
// Returns the (px, py) coordinates of the current pixel
41+
CUDA_CALLABLE cuda::std::pair<uint32_t, uint32_t> operator*() const;
42+
43+
// pre-increment operator that advances to the next pixel
44+
CUDA_CALLABLE Iterator& operator++();
45+
};
46+
47+
// the Sentinel class only needs to hold the stop value (i.e. final row)
48+
struct Sentinel {
49+
uint32_t py_end;
50+
51+
// stopping criterion: true if current row (py) reaches py_end
52+
CUDA_CALLABLE bool operator==(const Iterator& it) const;
53+
};
2054

21-
// Returns a 2D array of ray directions in camera frame in the specified pixel range
22-
// and store them in the provided buffer. By default, the full image is used
23-
template <MemoryLocation location>
24-
CUDA_CALLABLE Array2D<Vec3D, location>& get_ray_directions(Array2D<Vec3D, location>& buffer,
25-
uint32_t px_start = 0,
26-
uint32_t px_end = UINT32_MAX,
27-
uint32_t py_start = 0,
28-
uint32_t py_end = UINT32_MAX) const {
29-
for (auto i = max(0, px_start); i < min(height, px_end); ++i) {
30-
for (auto j = max(0, py_start); j < min(width, py_end); ++j) {
31-
buffer[i][j] = get_ray_direction(j, i);
32-
}
33-
}
34-
return buffer;
35-
}
55+
// range methods
56+
CUDA_CALLABLE Iterator begin() const;
57+
CUDA_CALLABLE Sentinel end() const;
3658
};

genmetaballs/src/cuda/core/forward.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#include <cuda_runtime.h>
33
#include <vector>
44

5-
constexpr NUM_BLOCKS dim3(10); // XXX madeup
6-
constexpr THREADS_PER_BLOCK dim3(10);
5+
constexpr auto NUM_BLOCKS = dim3(10); // XXX madeup
6+
constexpr auto THREADS_PER_BLOCK = dim3(10);
77

88
namespace FMB {
99

tests/cpp_tests/test_camera.cu

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@
1111

1212
namespace test_camera_gpu {
1313

14-
// CUDA kernel to call get_ray_directions on device with multiple threads
15-
// Each thread processes one row of the image
14+
// CUDA kernel to call get_ray_direction on device with multiple threads
15+
// Each thread processes one row of the image via PixelCoordRange
1616
__global__ void get_ray_directions_kernel(Intrinsics intrinsics,
1717
Array2D<Vec3D, MemoryLocation::DEVICE> ray_buffer) {
1818
uint32_t row_start = threadIdx.x * 2;
1919
uint32_t row_end = max(row_start + 2, intrinsics.height);
2020
uint32_t col_start = threadIdx.y * 2;
2121
uint32_t col_end = max(col_start + 2, intrinsics.width);
22-
intrinsics.get_ray_directions(ray_buffer, row_start, row_end, col_start, col_end);
22+
auto pixel_coords = PixelCoordRange{row_start, row_end, col_start, col_end};
23+
24+
for (auto [px, py] : pixel_coords) {
25+
ray_buffer[px][py] = intrinsics.get_ray_direction(px, py);
26+
}
2327
}
2428

2529
} // namespace test_camera_gpu

0 commit comments

Comments
 (0)