Commit 8840db8a authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: item-4 R2 native - pixel-LoG + gated subtract-avg kernels on the...

CLAUDE: item-4 R2 native - pixel-LoG + gated subtract-avg kernels on the resident render frame + camera-upload fast path

render_pix_log: NaN-aware LINEAR LoG (convolve2DLReLU form, fmaf throughout
so a same-order CPU std::fma replay is bit-exact) fused with the ruling-3.1
POST-LoG subtract-average (diff = LoG - avg resident detection frame, gated
EMA refresh, NaN-avg bootstrap seed) on the 640x512 sensor window of r_rbg.
render_avg_accum: NaN-aware raw-domain sum/cnt -> the next run's prior-
average file. Entries tp_proc_render_log_setup (ALL allocs at arm - the
no-alloc-class-in-loop rule; re-arm resets = rerun==fresh) and
tp_proc_render_log_get; render_scene enqueues both after render_dp on the
render stream, before render_done. KERNELS 45.

Enqueue shave: tp_proc_render_cameras fast path - scene meta+ERS only
(NULL radial/rbr = keep resident static tables), pinned double-buffered
staging + async copies on the render stream; no stream-sync, no per-scene
rByRDist H2D.

New case log_pipe (registered): L0 log-armed render bit-identical; L1 GPU ==
CPU float replay BIT-exact (raw/diff/avg/accum, 4 frames) + double oracle
rel 1.8e-7 of input mag; NaN substitution exercised; L2 prior base + median
gate, both populations non-empty; L3 rerun==fresh bit-identical; L4 camera
fast-path == full upload bit-exact. render_pipe --bench grows --log (R2
armed): render-8 re-verified 4.365 ms/scene in 8+8 carve, +0.15 with R2.
Full suite ALL PASS; compute-sanitizer 12.8 memcheck 0 errors.
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 0a0d5096
......@@ -73,6 +73,7 @@ case scene_dp_split test_pose_scene_dp_split_jna ${MODEL}/testdata/pose_corr
case u16_cond test_u16_cond_jna ${REPO} --tol 0 # T2 16-bit feeder: u16 staging + swap/RowCol/two-map kernel bit-exact vs host ref AND vs the float/preload path (synthetic) # By Claude on 07/18/2026
#case render_full test_render_full_jna ${MODEL}/testdata/pose_corr --tol 0 # R0 bench (ROADMAP item 4): full-frame combined-render chain ms/scene (timing, PASS=sanity only; run by name, pin clocks: perfctl hold measure -- ./run_cases.sh render_full) # By Claude on 07/19/2026
case render_pipe test_render_pipe_jna ${MODEL}/testdata/pose_corr --tol 0 # item-4 R1a: async full-frame render chain bit-exact vs blocking path + concurrency stress (add --bench 50 for ms/scene) # By Claude on 07/19/2026
case log_pipe test_log_pipe_jna ${MODEL}/testdata/pose_corr --tol 0 # item-4 R2: pixel-LoG + gated subtract-avg bit-exact vs CPU float replay + rel-tol vs double oracle; rerun==fresh; camera fast-path == full upload # By Claude on 07/19/2026
#case carve_combo test_carve_combo_jna ${MODEL}/testdata/pose_corr --tol 0 # item-4 step-3: pose DP in its green-ctx carve + render chain in a disjoint carve, one thread; bit-identity + latency percentiles (--pose-green/--render-green/--scenes) # By Claude on 07/19/2026
#case three_service test_three_service_jna ${MODEL}/testdata/pose_corr --tol 0 # item-4 MAP v2 concurrent measure: pose-8 + render-8 + LibTorch DNN thread in rest-20 (TPDNN_GREEN_SM); bit-identity + per-service latency/throughput (--dnn-green/--dnn-period/--no-dnn); needs jna/build_dnn.sh + the DNN models # By Claude on 07/19/2026
#case convert_direct test_convert_direct ${REPO}/testdata/convert_direct_legacy # local blessed goldens (~200MB, make_convert_direct_case.py)
......
This diff is collapsed.
......@@ -72,6 +72,7 @@ extern "C" {
int tp_proc_render_scene(void*, const float*, int, float,float,float,float, float,float, int,int, int, int);
int tp_proc_render_status(void*, int, int*);
int tp_proc_render_get(void*, float*);
int tp_proc_render_log_setup(void*, const float*, int, const float*, int, float, float, int, int); // item-4 R2 (--log bench) // By Claude on 07/19/2026
void tp_proc_destroy(void*);
void tp_destroy_module(void*);
}
......@@ -93,14 +94,15 @@ int main(int argc, char** argv){
std::string data_dir = "testdata/pose_corr";
std::string srcdir = "src";
std::string devrt = "/usr/local/cuda-12.8/lib64/libcudadevrt.a";
int bench = 0;
int bench = 0, with_log = 0;
for (int i = 1; i < argc; i++){
if (!strcmp(argv[i],"--data") && (i+1<argc)) data_dir = argv[++i];
else if (!strcmp(argv[i],"--bench") && (i+1<argc)) bench = atoi(argv[++i]);
else if (!strcmp(argv[i],"--log")) with_log = 1; // item-4 R2: arm LoG+subtract-avg for the bench // By Claude on 07/19/2026
else if (!strcmp(argv[i],"--src") && (i+1<argc)) srcdir = argv[++i];
else if (!strcmp(argv[i],"--devrt") && (i+1<argc)) devrt = argv[++i];
else if (!strcmp(argv[i],"--tol") && (i+1<argc)) i++; // tol is fixed 0 here; accepted for run_cases.sh uniformity
else { fprintf(stderr,"Usage: %s --data <pose_corr case> [--src <dir>] [--devrt <a>]\n", argv[0]); return EXIT_FAILURE; }
else { fprintf(stderr,"Usage: %s --data <pose_corr case> [--src <dir>] [--devrt <a>] [--bench N [--log]]\n", argv[0]); return EXIT_FAILURE; }
}
printf("%s: data=%s (NVRTC module from %s)\n", argv[0], data_dir.c_str(), srcdir.c_str());
TpTestData data(data_dir);
......@@ -239,6 +241,19 @@ int main(int argc, char** argv){
// ---- optional bench: the production async chain, launch -> completion ----
if (bench > 0){
if (with_log){ // item-4 R2: production-shaped taps (psf=1, n_sigma=4 -> krad 3), EMA on, accumulator on // By Claude on 07/19/2026
const int krad = 3, ksize = 2*krad+1;
std::vector<float> taps((size_t)ksize*ksize);
{ const double sigma = 1.0/std::sqrt(2.0), s2 = sigma*sigma; double sp=0, sn=0;
std::vector<double> d((size_t)ksize*ksize);
for (int y=-krad;y<=krad;y++) for (int x=-krad;x<=krad;x++){
const double r2=(double)x*x+y*y, raw=(1.0-r2/(2*s2))*std::exp(-r2/(2*s2));
d[(size_t)(y+krad)*ksize+(x+krad)]=raw; if (raw>0) sp+=raw; else sn+=raw; }
for (size_t i=0;i<d.size();i++) taps[i]=(float)(d[i]>0?d[i]/sp:d[i]/-sn); }
if(tp_proc_render_log_setup(p, taps.data(), krad, nullptr, 0, 0.02f, 10.f, 1, 0)){
printf("FAIL bench log_setup: %s\n", tp_last_error()); return EXIT_FAILURE; }
printf("bench: LoG + subtract-avg + accumulator ARMED (item-4 R2)\n");
}
for (int i = 0; i < 5; i++){ // warmup
tp_proc_render_scene(p, nullptr, 0, 0,0,0,0, 0,0, 0,0, -1, 0);
tp_proc_render_status(p, 1, nullptr);
......
This diff is collapsed.
......@@ -109,3 +109,89 @@ extern "C" __global__ void condition_lwir_u16(
scale_maps[map_index],
-offset_maps[map_index]);
}
// imagej ROADMAP item 4 rung R2: pixel LoG + post-LoG subtract-average on the
// resident render frame. See tp_condition.h for the frozen contract (Java
// oracle = CuasRTUtils.convolve2DLReLU LINEAR form on the sensor window; all
// accumulation through fmaf so a same-order CPU std::fma replay is bit-exact).
// By Claude on 07/19/2026.
extern "C" __global__ void render_pix_log(
const float * src,
int src_stride,
int x0,
int y0,
int width,
int height,
const float * taps,
int krad,
float * log_raw,
float * avg,
float * diff,
float ema_k,
float gate_abs)
{
const int x = (int) (blockIdx.x * blockDim.x + threadIdx.x);
const int y = (int) (blockIdx.y * blockDim.y + threadIdx.y);
if ((x >= width) || (y >= height)) return;
const int idx = y * width + x;
const float c = src[(size_t) (y0 + y) * src_stride + (x0 + x)];
float L;
if (isnan(c)) {
L = nanf("");
} else {
// same loop order as the Java oracle (iy outer, ix inner, top-left first)
float v = 0.0f;
const int ksize = 2 * krad + 1;
for (int iy = 0; iy < ksize; iy++) {
const int yy = y + iy - krad;
if ((yy < 0) || (yy >= height)) continue; // out-of-window taps contribute 0
const float * srow = src + (size_t) (y0 + yy) * src_stride + x0;
const float * trow = taps + iy * ksize;
for (int ix = 0; ix < ksize; ix++) {
const int xx = x + ix - krad;
if ((xx < 0) || (xx >= width)) continue;
const float d = srow[xx];
v = fmaf(isnan(d) ? c : d, trow[ix], v);
}
}
L = v;
}
if (log_raw) log_raw[idx] = L;
if (!avg) { diff[idx] = L; return; }
const float A = avg[idx];
if (isnan(L)) {
diff[idx] = nanf(""); // average keeps its last state
} else if (isnan(A)) {
if (ema_k > 0.0f) { avg[idx] = L; diff[idx] = 0.0f; } // bootstrap seed
else diff[idx] = nanf(""); // prior-file-only mode: never seen
} else {
const float d = L - A;
diff[idx] = d;
if ((ema_k > 0.0f) && ((gate_abs <= 0.0f) || (fabsf(d) <= gate_abs))) {
avg[idx] = fmaf(ema_k, d, A); // gated EMA refresh (fused axpy)
}
}
}
// R2 companion: raw-domain running average accumulation (next run's prior file).
// By Claude on 07/19/2026.
extern "C" __global__ void render_avg_accum(
const float * src,
int src_stride,
int x0,
int y0,
int width,
int height,
float * acc_sum,
float * acc_cnt)
{
const int x = (int) (blockIdx.x * blockDim.x + threadIdx.x);
const int y = (int) (blockIdx.y * blockDim.y + threadIdx.y);
if ((x >= width) || (y >= height)) return;
const int idx = y * width + x;
const float c = src[(size_t) (y0 + y) * src_stride + (x0 + x)];
if (!isnan(c)) {
acc_sum[idx] += c;
acc_cnt[idx] += 1.0f;
}
}
......@@ -66,4 +66,51 @@ extern "C" __global__ void condition_lwir_u16(
const float * scale_maps,
const float * offset_maps);
/* imagej ROADMAP item 4 rung R2 (07/19/2026): pixel-domain LoG + subtract-
* average on the GPU-resident detection-input render (r_rbg), enqueued on the
* render stream right after the render_dp chain. The math is the LINEAR
* (alpha=1) form of CuasRTUtils.convolve2DLReLU restricted to the sensor
* window (width x height at (x0,y0) inside the padded imclt buffer):
* - center NaN -> output NaN (a NaN neighbor never blooms into a valid pixel);
* - NaN tap -> substitute the center value (edge replication);
* - out-of-window tap -> contributes 0 (the Java TileNeibs margin behavior).
* Every product accumulates through fmaf() so a CPU float replay using
* std::fma in the same loop order is BIT-exact (the log_pipe case gate).
*
* Subtract-average runs in the POST-LoG domain (Andrey ruling 3.1: LoG is
* linear, so maintaining the average post-LoG is the same math, fused axpy):
* diff = LoG - avg; gated EMA refresh avg += ema_k*diff only where
* |diff| <= gate_abs, so slow-apparent-motion targets are never absorbed.
* avg NaN + LoG valid seeds avg = LoG (diff 0) when EMA is on - the no-prior-
* file bootstrap; with EMA off the diff stays NaN there (prior file only).
* By Claude on 07/19/2026. */
extern "C" __global__ void render_pix_log(
const float * src, // padded render buffer (r_rbg)
int src_stride, // floats per row
int x0, // window origin in src (DTT_SIZE/2)
int y0,
int width, // sensor window (640 x 512)
int height,
const float * taps, // (2*krad+1)^2 LoG taps, row-major
int krad,
float * log_raw, // may be NULL: raw LoG (debug/eval)
float * avg, // may be NULL: post-LoG average, updated in place
float * diff, // detection frame out: LoG - avg (or LoG when avg NULL)
float ema_k, // 0 = no EMA refresh
float gate_abs); // >0: update only where |diff| <= gate_abs
/* R2 companion: NaN-aware raw-domain accumulation of the rendered frames
* (sum += pixel, cnt += 1 where valid) over the run - D2H once after the loop
* builds the NEXT run's prior-average file (ruling 3.1: the base is a prior-
* run average, first rotations are unrepresentative). By Claude on 07/19/2026. */
extern "C" __global__ void render_avg_accum(
const float * src, // padded render buffer (r_rbg)
int src_stride,
int x0,
int y0,
int width,
int height,
float * acc_sum, // [height*width] running sum
float * acc_cnt); // [height*width] valid-sample count
#endif /* SRC_TP_CONDITION_H_ */
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