Accélérer le ray-tracing (SVF, openness) ~10x via accumulation des tangentes
This commit is contained in:
@ -298,10 +298,18 @@ def _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m=Non
|
||||
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.
|
||||
Optimisation : on accumule la TANGENTE de l'angle (dz/dist) au lieu de
|
||||
l'angle lui-même — atan étant strictement croissante, max(angles) =
|
||||
atan(max(tangentes)). L'arctan (coûteuse, pleine image) n'est donc plus
|
||||
appliquée qu'aux checkpoints de rayon, pas à chaque pas de rayon.
|
||||
Un seul couple de max cumulés est maintenu, snapshoté à chaque checkpoint
|
||||
(les rayons étant emboîtés, chaque checkpoint réutilisait avant le même
|
||||
calcul 3 fois). fmax ignore les NaN du padding : plus de nan_to_num/where.
|
||||
|
||||
Padding 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: CPU numpy array — filled DEM (no NaN), shape (rows, cols).
|
||||
@ -338,6 +346,12 @@ def _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m=Non
|
||||
# Free the CPU copy — we don't need it anymore
|
||||
del padded_np
|
||||
|
||||
# Checkpoints triés par pas : (step, r_idx). Les snapshots sont pris quand
|
||||
# le pas courant atteint le pas du checkpoint — les rayons ne dépassent
|
||||
# donc pas le plus grand checkpoint demandé (équivalent au break d'avant).
|
||||
checkpoints = sorted((radii_steps[r_idx], r_idx) for r_idx in range(n_radii))
|
||||
last_step = checkpoints[-1][0]
|
||||
|
||||
# Process one direction at a time to limit GPU memory.
|
||||
# Store results as flat CPU arrays — transfer back to GPU at the end.
|
||||
pos_results = [None] * n_dirs
|
||||
@ -346,9 +360,9 @@ def _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m=Non
|
||||
for d_idx in range(n_dirs):
|
||||
ddx, ddy = dx_dir[d_idx], dy_dir[d_idx]
|
||||
|
||||
# Pre-compute valid steps for this direction
|
||||
# Pre-compute valid steps for this direction (jusqu'au dernier checkpoint)
|
||||
valid_steps = []
|
||||
for step in range(1, max_dist + 1):
|
||||
for step in range(1, last_step + 1):
|
||||
px = int(round(ddx * step))
|
||||
py = int(round(ddy * step))
|
||||
dist_m = math.sqrt((ddx * step * res) ** 2 + (ddy * step * res) ** 2)
|
||||
@ -356,11 +370,11 @@ def _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m=Non
|
||||
continue
|
||||
valid_steps.append((step, px, py, dist_m))
|
||||
|
||||
# Running max angles per radius
|
||||
running_pos = xp.zeros((n_radii, rows, cols))
|
||||
running_neg = xp.zeros((n_radii, rows, cols))
|
||||
# Track which radius checkpoints have been passed
|
||||
radii_remaining = set(range(n_radii))
|
||||
# Max cumulé des tangentes (float32 : moitié de VRAM vs float64)
|
||||
running_pos = xp.zeros((rows, cols), dtype=np.float32)
|
||||
running_neg = xp.zeros((rows, cols), dtype=np.float32)
|
||||
snapshots = {}
|
||||
cp_queue = list(checkpoints)
|
||||
|
||||
for step, px, py, dist_m in valid_steps:
|
||||
# Slice from padded array, subtract original dem
|
||||
@ -369,34 +383,33 @@ def _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m=Non
|
||||
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)
|
||||
# Tangentes des angles (positive : terrain au-dessus, négative : en
|
||||
# dessous). fmax propage le non-NaN : le bord de padding ne compte
|
||||
# pas, comme avec l'ancien where(isnan) — en une seule opération.
|
||||
running_pos = xp.fmax(running_pos,
|
||||
xp.maximum(elev_diff, 0) / dist_m)
|
||||
running_neg = xp.fmax(running_neg,
|
||||
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:
|
||||
pos_angle_safe = xp.nan_to_num(pos_angle, nan=0)
|
||||
neg_angle_safe = xp.nan_to_num(neg_angle, nan=0)
|
||||
running_pos[r_idx] = xp.where(xp.isnan(pos_angle), running_pos[r_idx],
|
||||
xp.maximum(running_pos[r_idx], pos_angle_safe))
|
||||
running_neg[r_idx] = xp.where(xp.isnan(neg_angle), running_neg[r_idx],
|
||||
xp.maximum(running_neg[r_idx], neg_angle_safe))
|
||||
|
||||
# Check which radii have been passed
|
||||
new_remaining = set()
|
||||
for r_idx in radii_remaining:
|
||||
if step < radii_steps[r_idx]:
|
||||
new_remaining.add(r_idx)
|
||||
radii_remaining = new_remaining
|
||||
if not radii_remaining:
|
||||
# Snapshot du checkpoint atteint : conversion en angle UNE fois
|
||||
while cp_queue and step >= cp_queue[0][0]:
|
||||
_, r_idx = cp_queue.pop(0)
|
||||
snapshots[r_idx] = (xp.arctan(running_pos),
|
||||
xp.arctan(running_neg))
|
||||
if not cp_queue:
|
||||
break
|
||||
|
||||
# Checkpoints jamais atteints (steps invalides) : état final du balayage
|
||||
while cp_queue:
|
||||
_, r_idx = cp_queue.pop(0)
|
||||
snapshots[r_idx] = (xp.arctan(running_pos),
|
||||
xp.arctan(running_neg))
|
||||
|
||||
# Store results on CPU, free GPU memory before next direction
|
||||
pos_results[d_idx] = to_cpu(running_pos)
|
||||
neg_results[d_idx] = to_cpu(running_neg)
|
||||
del running_pos, running_neg
|
||||
pos_results[d_idx] = to_cpu(xp.stack([snapshots[r][0] for r in range(n_radii)]))
|
||||
neg_results[d_idx] = to_cpu(xp.stack([snapshots[r][1] for r in range(n_radii)]))
|
||||
del running_pos, running_neg, snapshots
|
||||
gpu_cleanup()
|
||||
|
||||
# Free the large padded array
|
||||
|
||||
Reference in New Issue
Block a user