Commit 39534100 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: DP rung D2 - device measure chain from ONE parent entry

PoseMeasureChain descriptor (tp_lma.h) = the rung-B3 per-scene pcyc_*
union + the resident buffer pointers the granular stage functions launch
with - the DP parent's marshalling, consumed verbatim. Geometry buffers
stay opaque (void*) so the header remains host-compilable; tp_lma.cu
casts at the launch sites (one NVRTC translation unit).

pose_measure_dp tail-enqueues the EXACT B3 host stage sequence:
task_update -> [slot0 rot-deriv + tile offsets + erase(NaN) + convert
SET] -> [slot1 rot-deriv + tile offsets + convert SUBTRACT] ->
consolidate -> inter-corr -> normalize -> peak. Payload kernels are the
UNMODIFIED production ones (most already single-thread CDP parents -
the pre-JNA top-DP pattern), so the chain is byte-identical BY
CONSTRUCTION; only the interior host syncs and the mid-chain 4-byte
count readbacks disappear. Mid-chain counts (MB pairs / surviving tiles
/ corr rows) are read by self-chaining launchers (the D1 pattern) that
run tail-enqueued BEHIND the counting kernel and check the LIVE count
against the descriptor's provisioned capacity (device count guards;
static over-provisioned shapes per the ratified D2 rule). New helper
kernels only move bytes: gather/scatter of the per-cam CLT slices and
the 0xFF NaN-poison fill (the host DtoD memcpys/memsets).

Wrapper tp_proc_exec_pose_measure_cycle_dp (B3-signature drop-in):
host keeps the rung-C2 nullable uploads (pose_task_upload split out of
exec_pose_task_update, same bytes), the per-sequence reverse-distortion
build, consolidate/peak capacity provisioning (no device-side malloc),
the B4 bayer fence, ONE parent launch, ONE end-of-chain 5-int status
D2H (rc/pairs/surviving/misaligned/corr rows). Peaks/corr indices stay
resident for the unchanged fetch calls.

Gate: test_pose_measure_dp_jna (cases.list 'measure_dp', rides the
captured pose_corr case for real images/kernels/geometry/center-TD;
task template txy/centers extracted from the captured production task
stream; camera pair synthetic per the B1/D1 two-instance rule). Three
poses, B3 entry vs DP chain, ORDER-INDEPENDENT KEYED BY PACKED INDEX:
counts/stats equal, index sets equal, raw-TD (256) + FZ-normalized PD
(225) + peak (8) rows ALL memcmp-equal (300 corr rows, 150/150 tiles).
run_cases.sh ALL PASS (pose_corr @tol 0, peak, peak_recenter, avg_td,
avg_td_oob, bayer_staged, dp_cycles, measure_dp); DP spike test still
PASS; compute-sanitizer 12.8 memcheck 0 errors.
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 66fc53a1
......@@ -66,6 +66,7 @@ case avg_td test_avg_td ${REPO}/testdata/synth4/avg_td
case avg_td_oob test_avg_td_oob ${REPO}/testdata/synth4/avg_td_oob --tol 0
case bayer_staged test_bayer_staged_jna ${REPO} --tol 0 # rung B4: synthetic, --data unused
case dp_cycles test_pose_lma_dp_cycles_jna ${REPO} --tol 0 # rung D1: synthetic, --data unused; DP K-cycle chain bit-exact vs host cycles # By Claude on 07/17/2026
case measure_dp test_pose_measure_dp_jna ${MODEL}/testdata/pose_corr --tol 0 # rung D2: device measure chain bit-exact vs B3 host-batched entry, keyed by packed index # By Claude on 07/17/2026
#case convert_direct test_convert_direct ${REPO}/testdata/convert_direct_legacy # local blessed goldens (~200MB, make_convert_direct_case.py)
# gen <name(s)> <generator command, run from ${REPO}>
......
This diff is collapsed.
This diff is collapsed.
......@@ -1237,6 +1237,193 @@ extern "C" __global__ void pose_lma_dp_cycles(const PoseDpCycles * c, int cycle)
}
}
// ── DP rung D2: device measure chain (ladder ratified 2026-07-16) ────────────
// pose_measure_dp tail-enqueues the EXACT host stage sequence of the rung-B3
// entry tp_proc_exec_pose_measure_cycle: task_update -> [slot0 rot-deriv +
// tile offsets + erase(NaN) + convert SET] -> [slot1 rot-deriv + tile offsets
// + convert SUBTRACT] -> consolidate -> inter-corr -> normalize -> peak.
// The payload kernels are the UNMODIFIED production ones; most are themselves
// single-thread CDP parents (Andrey's pre-JNA top-DP pattern), so this nests
// them one level deeper - byte-identical BY CONSTRUCTION, only the interior
// host syncs and the mid-chain 4-byte count readbacks disappear. Counters
// written mid-chain are read by the NEXT launcher (it runs tail-enqueued
// BEHIND the counting kernel) and checked against the descriptor's
// provisioned capacities (device count guards). By Claude on 07/17/2026.
// Contiguous [num_cams][slice] gather of the per-cam CLT slices (the device
// equivalent of exec_consolidate_device's per-cam DtoD memcpy loop).
extern "C" __global__ void pose_dp_gather_slices(
float * const * src, // per-cam CLT slice pointers
size_t slice, // floats per slice
float * dst) // contiguous [num_cams][slice]
{
const float * s = src[blockIdx.y];
float * d = dst + (size_t) blockIdx.y * slice;
for (size_t i = (size_t) blockIdx.x * blockDim.x + threadIdx.x; i < slice;
i += (size_t) gridDim.x * blockDim.x) {
d[i] = s[i];
}
}
// The consolidated average -> the cam-0 CLT slice (the setCltData(0,..) copy).
extern "C" __global__ void pose_dp_scatter_slice(
float * const * dst, // per-cam CLT slice pointers (cam 0 target)
const float * src,
size_t slice)
{
float * d = dst[0];
for (size_t i = (size_t) blockIdx.x * blockDim.x + threadIdx.x; i < slice;
i += (size_t) gridDim.x * blockDim.x) {
d[i] = src[i];
}
}
// Bit-pattern fill (the cudaMemset(0xFF) NaN poison of cons_td_avg).
extern "C" __global__ void pose_dp_fill_bits(
size_t n, // floats
unsigned int bits,
float * data)
{
unsigned int * d = (unsigned int *) data;
for (size_t i = (size_t) blockIdx.x * blockDim.x + threadIdx.x; i < n;
i += (size_t) gridDim.x * blockDim.x) {
d[i] = bits;
}
}
// Tail launcher: runs AFTER correlate2D_inter, reads the LIVE corr-row count,
// guards it, then enqueues normalize + peak with the exact host arguments.
extern "C" __global__ void pose_measure_dp_tail(const PoseMeasureChain * c)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
const int nct = *c->num_corr_tiles;
c->status[4] = nct;
if (nct <= 0) { c->status[0] = -18; return; } // = B3 -18 (no corr tiles)
if (nct > c->corr_capacity) { c->status[0] = -24; return; } // device count guard
corr2D_normalize<<<1, 1, 0, cudaStreamTailLaunch>>>(
nct, c->dstride_corr_td, c->corrs_td, (float *) 0,
c->dstride_corr, c->corrs, c->fat_zero2, c->corr_radius);
if (c->use_float) {
corr2D_peak_eig_f<<<nct, 256, 0, cudaStreamTailLaunch>>>(
nct, c->corr_indices, c->pair_select, c->dstride_corr, c->corrs,
c->corr_radius, c->abs_min, c->rel_min, c->min_peak, c->eig_sub_frac,
c->refine, c->eig_sub_frac1, c->scale_axes, c->inc_axes,
c->peak_debias, c->peaks);
} else {
corr2D_peak_eig<<<nct, 256, 0, cudaStreamTailLaunch>>>(
nct, c->corr_indices, c->pair_select, c->dstride_corr, c->corrs,
c->corr_radius, c->abs_min, c->rel_min, c->min_peak, c->eig_sub_frac,
c->refine, c->eig_sub_frac1, c->scale_axes, c->inc_axes,
c->peak_debias, c->peaks);
}
c->status[0] = nct; // npk == nct (exec_corr2d_peak return convention)
}
// Average launcher: reads the LIVE surviving-tile count, averages, copies the
// average into cam 0 and starts the inter correlation (which resets its own
// counter device-side), then chains the tail.
extern "C" __global__ void pose_measure_dp_avg(const PoseMeasureChain * c)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
const int num_out = c->cons_counters[1];
c->status[2] = num_out;
c->status[3] = c->cons_counters[2];
if (num_out > 0) {
clt_average_sensors_list<<<dim3((unsigned) num_out, (unsigned) c->num_colors, 1),
TD_TILE_FLOATS, 0, cudaStreamTailLaunch>>>(
c->num_cams, c->cons_td_sens, c->tilesx, c->tilesy, c->num_colors,
c->cons_tasks_out, c->task_size, num_out, c->cons_td_avg, (float *) 0);
}
pose_dp_scatter_slice<<<1024, 256, 0, cudaStreamTailLaunch>>>(
c->clt, c->cons_td_avg, c->slice);
correlate2D_inter<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->num_cams, c->sel_sensors, c->clt, c->clt_ref, c->num_colors,
c->sc[0], c->sc[1], c->sc[2], c->ftasks1, c->num_tasks, c->tilesx,
c->corr_indices, c->num_corr_tiles, c->dstride_corr_td, c->corrs_td);
pose_measure_dp_tail<<<1, 1, 0, cudaStreamTailLaunch>>>(c);
}
// OOB launcher: reads the LIVE MB-pair count and runs the margin/priority pass.
extern "C" __global__ void pose_measure_dp_oob(const PoseMeasureChain * c)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
const int num_pairs = c->cons_counters[0];
c->status[1] = num_pairs;
if (num_pairs > 0) {
consolidate_oob<<<(num_pairs + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
c->task_size, c->num_cams, c->cons_pairs, num_pairs,
(float) c->img_w, (float) c->img_h, c->soft_margin, c->hard_margin,
c->cons_tasks_out, c->cons_counters + 1);
}
pose_measure_dp_avg<<<1, 1, 0, cudaStreamTailLaunch>>>(c);
}
// Consolidate launcher: runs AFTER both converts; zeroes the counters (the
// host cudaMemset), gathers/poisons the scratch and indexes the MB pairs.
extern "C" __global__ void pose_measure_dp_consolidate(const PoseMeasureChain * c)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
c->cons_counters[0] = 0;
c->cons_counters[1] = 0;
c->cons_counters[2] = 0;
pose_dp_gather_slices<<<dim3(1024, (unsigned) c->num_cams, 1), 256, 0, cudaStreamTailLaunch>>>(
c->clt, c->slice, c->cons_td_sens);
pose_dp_fill_bits<<<1024, 256, 0, cudaStreamTailLaunch>>>(
c->slice, 0xFFFFFFFFu, c->cons_td_avg);
index_consolidate<<<(c->num_tasks + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
c->task_size, c->ftasks0, c->ftasks1, c->num_tasks,
c->cons_pairs, c->cons_counters + 0, c->cons_counters + 2);
pose_measure_dp_oob<<<1, 1, 0, cudaStreamTailLaunch>>>(c);
}
// The D2 parent: ONE device entry for the whole measure cycle. Resolves the
// CURRENT pose set LIVE (rung-B device-authoritative index; equals the host
// mirror at entry today, and stays correct when D3 interleaves accept flips).
extern "C" __global__ void pose_measure_dp(const PoseMeasureChain * c)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
c->status[0] = 0; c->status[1] = 0; c->status[2] = 0;
c->status[3] = 0; c->status[4] = 0;
const float * pose_vectors = c->set_vectors[*c->current_set];
pose_task_update<<<(c->num_tasks + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
c->num_tasks, c->task_size, c->cameras,
c->ref_radial, c->ref_rbr, c->ref_ers,
c->scene_radial, c->scene_ers,
pose_vectors, c->task_map, c->task_centers,
c->disparity_corr, c->min_px, c->max_px, c->min_py, c->max_py,
c->task_code_main, c->task_code_sub,
c->dx_main, c->dy_main, c->dx_sub, c->dy_sub,
c->scale_main, c->scale_sub,
c->ftasks0, c->ftasks1);
// slot 0 (MB main): geometry -> erase(NaN) -> convert SET
calc_rot_deriv<<<c->num_cams, dim3(3, 3, 3), 0, cudaStreamTailLaunch>>>(
c->num_cams, (struct corr_vector *) c->cv, (trot_deriv *) c->rot);
calculate_tiles_offsets<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->uniform_grid, c->num_cams, c->ftasks0, c->num_tasks,
(struct gc *) c->gc, (struct corr_vector *) c->cv,
(float *) c->rbr, (trot_deriv *) c->rot);
erase_clt_tiles<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->num_cams, c->num_colors, c->tilesx, c->tilesy, c->clt, c->erase_fill);
convert_direct<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->num_cams, c->num_colors, c->kernel_offsets, c->kernels, c->images,
c->ftasks0, c->clt, c->dstride_img, c->num_tasks, 0,
c->img_w, c->img_h, c->kernels_hor, c->kernels_vert,
c->active, c->num_active, c->tilesx);
// slot 1 (MB partner): geometry -> convert SUBTRACT (negative scale in tasks)
calc_rot_deriv<<<c->num_cams, dim3(3, 3, 3), 0, cudaStreamTailLaunch>>>(
c->num_cams, (struct corr_vector *) c->cv, (trot_deriv *) c->rot);
calculate_tiles_offsets<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->uniform_grid, c->num_cams, c->ftasks1, c->num_tasks,
(struct gc *) c->gc, (struct corr_vector *) c->cv,
(float *) c->rbr, (trot_deriv *) c->rot);
convert_direct<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->num_cams, c->num_colors, c->kernel_offsets, c->kernels, c->images,
c->ftasks1, c->clt, c->dstride_img, c->num_tasks, 0,
c->img_w, c->img_h, c->kernels_hor, c->kernels_vert,
c->active, c->num_active, c->tilesx);
pose_measure_dp_consolidate<<<1, 1, 0, cudaStreamTailLaunch>>>(c);
}
#endif // __CUDACC_RTC__
extern "C" __global__ void lma_normal_products(
......
......@@ -311,6 +311,69 @@ typedef struct {
float * trace; // [num_cycles][POSE_LMA_RESIDENT_RESULT], NULL = off
} PoseDpCycles;
// ── DP rung D2 (ladder ratified 2026-07-16): device measure chain ────────────
// PoseMeasureChain = the rung-B3 per-scene descriptor (pcyc_*) plus the
// resident buffer pointers the granular stage functions launch with - the DP
// parent's marshalling, consumed verbatim. Geometry buffers are OPAQUE (void*)
// so this header stays host-compilable without the geometry structs; tp_lma.cu
// casts at the launch sites (same NVRTC translation unit). Counts produced
// MID-CHAIN (MB pairs / surviving tiles / corr rows) are read by small
// self-chaining launchers (the D1 pattern: each runs tail-enqueued BEHIND the
// counting kernel, so it sees the LIVE counter) and are bounded by the
// descriptor's provisioned capacities - the device count guards; the corr-row
// and tile counts are per-sequence constants, so the static shapes hold.
// By Claude on 07/17/2026.
typedef struct {
// shapes
int num_cams, num_colors, tilesx, tilesy;
int num_tasks, task_size;
int img_w, img_h, kernels_hor, kernels_vert;
int uniform_grid; // 0 = use provided centers (the lean chain)
int corr_capacity; // corr rows peaks/status are provisioned for (count guard)
// task-update policy (per-scene uniform-MB descriptor, = pcyc_*)
float disparity_corr, min_px, max_px, min_py, max_py;
int task_code_main, task_code_sub;
float dx_main, dy_main, dx_sub, dy_sub, scale_main, scale_sub;
// consolidate / correlate / normalize / peak policy (= pcyc_*)
float erase_fill; // host NAN (slot-0 SET semantics, erase_clt=1)
float soft_margin, hard_margin;
int sel_sensors; float sc[3];
float fat_zero2; // host float((double)fz*fz), = exec_corr2d_normalize
int corr_radius;
int pair_select; float abs_min, rel_min, min_peak, eig_sub_frac;
int refine; float eig_sub_frac1, scale_axes, inc_axes; int use_float;
// camera/template state (resident, rung-C2 registers)
const PoseCameraMeta * cameras;
const float * ref_radial; const float * ref_rbr; const float * ref_ers;
const float * scene_radial; const float * scene_ers;
const int * task_map; const float * task_centers;
// pose: fixed physical slots + live set index (rung B; D3-readiness - the
// parent resolves the CURRENT set exactly like pose_lma_dp_cycles)
float * set_vectors[2];
int * current_set;
// geometry buffers (struct gc / corr_vector / trot_deriv in the TU)
void * gc; void * cv; void * rbr; void * rot;
// resident task slots + convert inputs
float * ftasks0; float * ftasks1;
float ** kernel_offsets; float ** kernels; float ** images;
float ** clt; float ** clt_ref;
size_t dstride_img; // floats
int * active; int * num_active;
// consolidate scratch (host prepare_consolidate provisioned)
float * cons_td_sens; float * cons_td_avg;
float * cons_pairs; float * cons_tasks_out;
int * cons_counters; // [0] pairs, [1] surviving tiles, [2] misaligned
size_t slice; // floats per cam CLT slice
// correlation / peak (resident)
int * corr_indices; int * num_corr_tiles;
size_t dstride_corr_td; float * corrs_td;
size_t dstride_corr; float * corrs;
const float * peak_debias; float * peaks;
int * status; // [0] rc (corr rows or stage-tagged negative),
// [1] pairs, [2] surviving, [3] misaligned, [4] corr rows
} PoseMeasureChain;
#define POSE_MEASURE_STATUS_INTS 5
extern "C" __global__ void lma_normal_products(
int num_params,
int num_values,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment