Fix ray-tracing OOM + NO_BINARY: pad DEM on CPU, free GPU intermediates

This commit is contained in:
Antoine Jacquin
2026-05-31 19:36:29 +02:00
parent 2ccedbb9e0
commit deed10ea62

View File

@ -298,6 +298,11 @@ def _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
recording the max upward angle (positive openness) and max downward angle
(negative openness) reached at each radius checkpoint.
Padding is done on CPU (numpy) to avoid GPU memory pressure and
pre-compiled kernel mismatches (CUDA_ERROR_NO_BINARY_FOR_GPU on sm_89).
The padded array is transferred to GPU once, then each direction is
processed and results are streamed back to CPU.
Args:
dem: GPU or CPU filled DEM array (rows, cols).
rows, cols: dimensions.
@ -322,7 +327,15 @@ def _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
radii_steps = [max_dist]
n_radii = 1
padded = xp.pad(dem, max_dist, mode='constant', constant_values=xp.nan)
# Pad on CPU (numpy) — avoids GPU memory pressure and
# pre-compiled kernel issues (NO_BINARY_FOR_GPU on sm_89).
dem_np = to_cpu(dem)
padded_np = np.pad(dem_np, max_dist, mode='constant', constant_values=np.nan)
# Transfer padded DEM to GPU for computation
padded = to_gpu(padded_np)
# Free the CPU copy — we don't need it anymore
del dem_np, padded_np
# Process one direction at a time to limit GPU memory.
# Store results as flat CPU arrays — transfer back to GPU at the end.
@ -349,13 +362,17 @@ def _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
radii_remaining = set(range(n_radii))
for step, px, py, dist_m in valid_steps:
elev_diff = padded[max_dist + py:max_dist + py + rows,
max_dist + px:max_dist + px + cols] - dem
# Slice from padded array, subtract original dem
view = padded[max_dist + py:max_dist + py + rows,
max_dist + px:max_dist + px + cols]
elev_diff = view - dem
del view # free slice reference
# Positive: angle to terrain above viewer
pos_angle = xp.arctan2(xp.maximum(elev_diff, 0), dist_m)
# Negative: angle to terrain below viewer
neg_angle = xp.arctan2(xp.maximum(-elev_diff, 0), dist_m)
del elev_diff # free intermediate
# Update running max for all radius checkpoints still active
for r_idx in radii_remaining: