package com.elphel.imagej.gpu.jna;

import com.sun.jna.Library;
import com.sun.jna.Pointer;

/**
 * JNA boundary to the native tile-processor library (libtileproc.so) for the JCuda->JNA migration.
 * Stage-0b surface only: prove module compile/load across JNA. The full set (instances, set/exec/get)
 * follows the design doc handoffs/2026-06-25_JCuda-to-JNA-migration-design.md. By Claude on 2026-06-25.
 */
public interface TpJna extends Library {
    /** NVRTC-compile the kernels in srcdir (+ getTpDefines), cuLink(libcudadevrt), load the module.
     *  Returns an opaque module handle, or null on failure (see tp_last_error()). */
    Pointer tp_create_module(String srcdir, String libcudadevrt);
    /** item-4 step-4: same as tp_create_module, with an EXPLICIT green-context SM-carve spec
     *  ("&lt;count&gt;" | "&lt;skip&gt;+&lt;count&gt;" | "&lt;skip&gt;+rem"; null/""/"0" = plain context).
     *  By Claude on 07/19/2026. */
    Pointer tp_create_module2(String srcdir, String libcudadevrt, String greenSpec);
    /** Number of kernel functions resolved in the module (33 expected:
     *  19 base + 2 conditioning + 4 consolidation + 2 peak + pose-fx/J + 4 pose-LMA + LMA-products),
     *  or -1 if handle is null. */
    int     tp_module_num_functions(Pointer module);
    /** Last error message (NVRTC/CUDA log), empty if none. */
    String  tp_last_error();
    /** Free the module (cuModuleUnload + cuCtxDestroy). */
    void    tp_destroy_module(Pointer module);

    // ---- Stage 1: instance + geometry path ----
    /** Create a per-config GPU instance (allocates geometry device buffers). Null on failure. */
    Pointer tp_create_instance(Pointer module);
    /** Upload gc.expandSensors(16).toFloatArray() (n floats) to gpu_geometry_correction. 0 on success. */
    int     tp_set_geometry_correction(Pointer instance, float[] fgc, int n);
    /** Upload cv.toFullRollArray() (n floats) to gpu_correction_vector. 0 on success. */
    int     tp_set_correction_vector(Pointer instance, float[] fcv, int n);
    /** Run calcReverseDistortionTable -> gpu_rByRDist. 0 on success. */
    int     tp_exec_calc_reverse_distortions(Pointer instance);
    /** Run calc_rot_deriv(num_cams) -> gpu_rot_deriv. 0 on success. */
    int     tp_exec_rot_derivs(Pointer instance, int num_cams);
    /** Read gpu_rByRDist back into out[RBYRDIST_LEN=5001]. 0 on success. */
    int     tp_get_rbyrdist(Pointer instance, float[] out);
    /** Read gpu_rot_deriv back into out[5*16*3*3=720]. 0 on success. */
    int     tp_get_rot_deriv(Pointer instance, float[] out);
    /** Free the instance device buffers. */
    void    tp_destroy_instance(Pointer instance);

    // ---- Stage 2: convert_direct selftest ----
    /** Run the full convert_direct path (mirrors TpHostGpu allTests convert chain) against the
     *  tile_processor_gpu/clt golden data under data_root. lwir!=0 -> 16-cam LWIR set.
     *  Writes max |CLT-golden| into outMaxErr[0] and the kernel-computed active-tile count into
     *  outNumActive[0]. Returns 0 on success. */
    int     tp_convert_direct_selftest(Pointer module, int lwir, String data_root,
                                       double[] outMaxErr, int[] outNumActive);

    // ---- Stage 3: convert_direct + imclt_rbg_all ----
    /** Run convert_direct then imclt_rbg_all; compare CLT to clt/aux_chnN.clt (outCltErr[0]) and
     *  the de-pitched RBG to clt/aux_chnN.rbg (outRbgErr[0]). Returns 0 on success. */
    int     tp_imclt_selftest(Pointer module, int lwir, String data_root,
                              double[] outCltErr, double[] outRbgErr, int[] outNumActive);

    // ---- Stage 4: correlate2D + corr2D_combine + corr2D_normalize ----
    /** Run convert_direct then the intra TD-correlation quad path; compare CLT to clt/aux_chnN.clt
     *  (outCltErr[0]) and the quad-combined correlation to clt/aux_corr-quad.corr (outCorrErr[0]). */
    int     tp_corr_selftest(Pointer module, int lwir, String data_root,
                             double[] outCltErr, double[] outCorrErr, int[] outNumActive);

    // ---- Stage 5: textures_nonoverlap (DP; CDP-launches textures_accumulate) ----
    /** Run convert_direct then textures_nonoverlap; compare CLT to clt/aux_chnN.clt (outCltErr[0])
     *  and the de-interleaved diff_rgb_combo to clt/aux_diff_rgb_combo_dp.drbg (outTexErr[0]). */
    int     tp_tex_selftest(Pointer module, int lwir, String data_root,
                            double[] outCltErr, double[] outTexErr, int[] outNumActive);

    // ---- Step 1: persistent granular API (TpProc) — production-facing surface ----
    Pointer tp_proc_create(Pointer module);
    int     tp_proc_setup(Pointer proc, int num_cams, int num_colors, int img_w, int img_h, int kernels_hor, int kern_tiles);
    int     tp_proc_set_geometry(Pointer proc, float[] fgc, int n);
    int     tp_proc_set_correction_vector(Pointer proc, float[] fcv, int n);
    int     tp_proc_set_kernels(Pointer proc, int cam, float[] d, int n);
    int     tp_proc_set_kernel_offsets(Pointer proc, int cam, float[] d, int n);
    int     tp_proc_set_image(Pointer proc, int cam, float[] d);
    // ---- 3-B rung B4: pinned staged bayer upload (async per-sensor H2D). By Claude on 07/16/2026. ----
    /** Pinned host staging for slot (0/1): num_cams*img_w*img_h floats, lazily allocated
     *  together with the dedicated non-blocking copy stream + fence event. Null on failure. */
    Pointer tp_proc_bayer_staging(Pointer proc, int slot);
    /** Enqueue the pinned->device async copy of ONE sensor image from staging slot and
     *  return immediately (per-sensor copy/compute pipelining). Image consumers fence
     *  natively, so a partial upload is never visible to kernels or readbacks. */
    int     tp_proc_set_image_staged(Pointer proc, int cam, int slot);
    // ---- T2 16-bit feeder (07/18/2026): pinned u16 staging + one-pass GPU
    // uint16->float + Row/Col + two-map conditioning. By Claude on 07/18/2026. ----
    /** Pinned host u16 staging for slot (0/1): num_cams*img_w*img_h uint16 - the feeder
     *  preads raw TIFF strip bytes straight into it (no heap copy). Null on failure. */
    Pointer tp_proc_bayer_staging_u16(Pointer proc, int slot);
    /** Pinned host staging for the borrowed per-scene Row/Col doubles:
     *  rows [cam][img_w] | cols [cam][img_h] | inv_scales [cam]. Null on failure. */
    Pointer tp_proc_rowcol_staging(Pointer proc, int slot);
    /** Enqueue the async H2D of ONE sensor's u16 image from staging slot into the device
     *  u16 scratch (copy stream; consumed by tp_proc_exec_conditioning_u16). */
    int     tp_proc_set_image_staged_u16(Pointer proc, int cam, int slot);
    /** Enqueue the async H2D of the whole rowcol staging block (copy stream). */
    int     tp_proc_set_rowcol_staged(Pointer proc, int slot);
    /** Convert+condition the staged u16 scene into the ACTIVE image set on the copy
     *  stream (async; records the set's ready event - the D5a ONE-event contract). */
    int     tp_proc_exec_conditioning_u16(Pointer proc, int swapBytes, int rowcolEnable);
    /** De-pitch one resident source image into out[width*height]. */
    int     tp_proc_get_image(Pointer proc, int cam, float[] out);
    /** Resident production calibration maps, contiguous [camera][height][width]. */
    int     tp_proc_set_conditioning_maps(Pointer proc, float[] scales, float[] offsets, int numPixels);
    /** Java-expression validation calibration; FPN is contiguous [camera][height][width]. */
    int     tp_proc_set_conditioning_oracle(Pointer proc, double[] scales, double[] offsets,
                                            double[] scales2, double[] fpn, int numPixels);
    /** Condition all resident source images in place; useDouble=0 selects production two-map float. */
    int     tp_proc_exec_conditioning(Pointer proc, int useDouble, double dcLevel);
    /** use_center_image: broadcast one center image to all sensors (FPN back-prop mode). */
    int     tp_proc_set_center_image(Pointer proc, float[] d);
    int     tp_proc_set_tasks(Pointer proc, float[] ftasks, int ntiles, int totalFloats);
    int     tp_proc_get_tasks(Pointer proc, float[] out, int totalFloats);
    /** Explicit persistent task slots (0/1); the selected slot becomes the live kernel stream. */
    int     tp_proc_set_tasks_slot(Pointer proc, float[] ftasks, int ntiles, int totalFloats, int slot);
    int     tp_proc_get_tasks_slot(Pointer proc, float[] out, int totalFloats, int slot);
    /** 3-B rung B1: make a resident slot the live kernel task stream with NO upload
     *  (after pose_task_update rewrote it in place). By Claude on 07/16/2026. */
    int     tp_proc_activate_tasks_slot(Pointer proc, int slot);
    int     tp_proc_exec_geometry(Pointer proc, int uniformGrid);
    /** convert_direct: ref_scene (0/1 -> clt vs clt_ref), erase_clt (-1/0/1), no_kernels (0/1). */
    int     tp_proc_exec_convert_direct(Pointer proc, int refScene, int eraseClt, int noKernels);
    int     tp_proc_get_clt(Pointer proc, int cam, int refScene, float[] out);
    int     tp_proc_set_clt(Pointer proc, int cam, float[] data, int refScene);
    /** On-device 16-sensor TD consolidation (tp_consolidate.cu v1 chain, roadmap 2c):
     *  index_consolidate -> consolidate_oob (soft/hard margins, px; 0/0 = v0 no filtering) ->
     *  clt_average_sensors_list; the average lands in the cam-0 CLT slice (the per-sensor TD
     *  never leaves the device — replaces the getCltData/consolidateSensorsTD/setCltData CPU
     *  bridge). ftasks0/1 = flattened TpTask streams (TpTask.asFloatArray), ftasks1 null = no-MB.
     *  stats (int[3], may be null) = {MB pairs kept, surviving tiles, misaligned pairs}.
     *  0 on success. By Claude on 07/12/2026. */
    int     tp_proc_exec_consolidate(Pointer proc, float[] ftasks0, float[] ftasks1,
                                     int numEntries, int taskSize,
                                     float softMargin, float hardMargin, int[] stats);
    /** Same consolidation chain, consuming post-geometry task slots directly; slot1 < 0 = no-MB. */
    int     tp_proc_exec_consolidate_slots(Pointer proc, int slot0, int slot1,
                                           int numEntries, int taskSize,
                                           float softMargin, float hardMargin, int[] stats);
    /** Allocate imclt(RBG) + correlation buffers and store corr config. */
    int     tp_proc_setup_rbg_corr(Pointer proc, int numPairs, int s0,int s1,int s2,int s3,
                                   float cw0,float cw1,float cw2, int corrOutRad);
    int     tp_proc_exec_imclt(Pointer proc, int applyLpf, int useRef);
    int     tp_proc_get_rbg(Pointer proc, int cam, float[] out);
    int     tp_proc_exec_corr2d(Pointer proc, double fatZero);
    /** de-pitch quad correlation; returns num_corr_combo. */
    int     tp_proc_get_corr2d_combo(Pointer proc, float[] out);

    // ---- granular correlations (match GpuQuad's separate exec calls) ----
    int     tp_proc_exec_corr2d_td(Pointer proc, int s0,int s1,int s2,int s3, float sc0,float sc1,float sc2);
    int     tp_proc_exec_corr2d_inter_td(Pointer proc, int selSensors, float sc0,float sc1,float sc2);
    int     tp_proc_exec_corr2d_combine(Pointer proc, int initCorrCombo, int numPairs, int pairsMask);
    int     tp_proc_exec_corr2d_normalize(Pointer proc, int combo, double fatZero, int corrRadius);
    int     tp_proc_get_corr2d(Pointer proc, float[] out, int corrRad);
    // ---- peak measurement (tp_peak.cu corr2D_peak_eig{,_f}, roadmap 3-C rung 2) ---- // By Claude on 07/14/2026
    /** Optional envelope de-bias array ((2*corrRad+1)^2 floats); len 0/null clears -> kernel gets NULL. */
    int     tp_proc_set_peak_debias(Pointer proc, float[] data, int len);
    /** Frozen 15-arg getMaxXYCmEig contract on the normalized PD buffer (run AFTER
     *  tp_proc_exec_corr2d_normalize combo=0): one {dx,dy,str,e0x,e0y,l0,l1,valid} row per
     *  resident corr tile. pairSelect 0xff = lean sum slots (-1 = all); useFloat: 0 = double
     *  (oracle-matched validation tier), 1 = float (production - ratified 07/14/2026, real-case
     *  float-vs-double dx/dy <= 7.2e-7 px). Returns the row count or <0. */
    int     tp_proc_exec_corr2d_peak(Pointer proc, int pairSelect,
                                     float absMin, float relMin, float minPeak, float eigSubFrac,
                                     int refine, float eigSubFrac1, float scaleAxes, float incAxes,
                                     int useFloat);
    /** D2H the peak rows (count x 8 floats). Returns the count. */
    int     tp_proc_get_peaks(Pointer proc, float[] out);
    /** Primitive-float raw pose fx/J, before eigen transforms. Camera metadata is
     *  {width,height,focal,pixel,distortionRadius,disparityRadius,lineTime,stepR}. */
    int     tp_proc_exec_pose_fx_jacobian(
                                      Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen,
                                      float[] poseVectors, float[] centers, byte[] selection,
                                      int numTiles, int calculateJacobian,
                                      float[] fx, float[] jt, byte[] valid);
    /** One-thread fixed three-angle float candidate calculation. Result layout is
     *  {valid, raw H[9], raw b[3], delta[3], candidate[3]}. */
    int     tp_proc_exec_pose_lma_step(Pointer proc,
                                      float lambda, float[] weights, float[] jt,
                                      float[] ymfxWeighted, float[] vector,
                                      int numValues, float[] result);
    /** Continue from raw fx/J left by the most recent pose kernel. Result has
     *  the 19 candidate floats, current/candidate full/pure RMS, then accepted/stop.
     *  Prepared arrays are nullable unless capturePrepared is nonzero. */
    int     tp_proc_exec_pose_lma_resident_step(Pointer proc,
                                      float lambda, float[] weights, float[] y,
                                      float[] eigen, float[] vector, float pureWeight,
                                      float rmsDiff, int numTiles, int capturePrepared, float[] result,
                                      float[] preparedJt, float[] preparedYmfx);
    /** Rung C1 (design 2026-07-16): resident prepareLMA. Assembles y/weights/eigen
     *  from the RESIDENT peak/corr-index buffers, projects fx/J at the current pose
     *  (no readback) and normalizes weights on-device. prepResult layout:
     *  {valid, numMeas, sumWRaw, pureWeight, fullWeight, regW0, regW1, regW2}.
     *  outY/outWeights/outEigen nullable unless capture != 0 (one-shot oracle).
     *  By Claude on 07/17/2026. */
    int     tp_proc_exec_pose_lma_prepare_resident(Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen,
                                      float[] poseVectors, float[] centers,
                                      int numTiles, int numComponents,
                                      int corrSlot, int ntileShift,
                                      int useEigen, float eigMinSqrt, float eigMaxSqrt,
                                      float minConfidence, int sameWeights,
                                      float[] pullTargets, float[] paramRegweights,
                                      int light, // rung C3: 1 = frozen conditioning, fresh y only // By Claude on 07/17/2026
                                      float[] prepResult, int capture,
                                      float[] outY, float[] outWeights, float[] outEigen);
    /** 3-B rung B1 (design 2026-07-16): resident task build. pose_task_update runs the
     *  C1-validated projection chain over the per-sequence task template and rewrites
     *  the 6-float task headers of BOTH resident slots in place. Nullable groups follow
     *  the rung-C2 configuration-register contract: camera blocks (shared with
     *  prepare_resident), taskMap/taskCenters (per-sequence template), poseVectors
     *  (null = device-authoritative current set). Task slots must hold the
     *  per-sequence skeleton (set_tasks_slot) with exactly numTasks entries.
     *  By Claude on 07/16/2026. */
    int     tp_proc_exec_pose_task_update(Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen,
                                      float[] poseVectors, int[] taskMap, float[] taskCenters,
                                      int numTasks, int numCams,
                                      float disparityCorr,
                                      float minPx, float maxPx, float minPy, float maxPy,
                                      int taskCodeMain, int taskCodeSub,
                                      float dxMain, float dyMain, float dxSub, float dySub,
                                      float scaleMain, float scaleSub);
    // ---- 3-B rung B3: single measure-chain entry (design 2026-07-16). By Claude on 07/16/2026. ----
    /** Store the per-scene measure-cycle descriptor: the union of the fixed chain's
     *  stage arguments (task_update / consolidate / inter-corr / normalize / peak). */
    int     tp_proc_set_pose_cycle(Pointer proc,
                                      int numTasks, int taskSize,
                                      float disparityCorr,
                                      float minPx, float maxPx, float minPy, float maxPy,
                                      int taskCodeMain, int taskCodeSub,
                                      float dxMain, float dyMain, float dxSub, float dySub,
                                      float scaleMain, float scaleSub,
                                      float softMargin, float hardMargin,
                                      int selSensors, float sc0, float sc1, float sc2,
                                      double fatZero, int corrRadius,
                                      int pairSelect,
                                      float absMin, float relMin, float minPeak, float eigSubFrac,
                                      int refine, float eigSubFrac1, float scaleAxes, float incAxes,
                                      int useFloat);
    /** ONE JNA crossing per measure cycle: pose_task_update -> [slot0 geometry+convert(erase)]
     *  -> [slot1 geometry+convert(subtract)] -> consolidate_slots -> inter-corr -> normalize
     *  -> peak, calling the EXACT granular stage functions in the exact order (byte-identical
     *  by construction). Nullable groups forward verbatim to pose_task_update. stats (int[3],
     *  may be null) = consolidation {pairs, surviving, misaligned}. Returns the corr-tile
     *  count (>0) or a stage-tagged negative (-10..-20, see tp_jna.cpp). */
    int     tp_proc_exec_pose_measure_cycle(Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen,
                                      float[] poseVectors, int[] taskMap, float[] taskCenters,
                                      int[] stats);
    /** DP rung D4 (ladder ratified 2026-07-16): the FULL per-scene pose solve as ONE
     *  device entry - numCycles x [D2 measure chain -> full/light prepare -> six-stage
     *  resident step -> commit], self-chained on-device with zero interior host round
     *  trips. Camera/task/centers groups follow the C2 configuration-register contract
     *  (null = keep resident). pull/regw are the full-prepare objective rows (null =
     *  keep resident); vector = the entry anchor [3]. Readbacks: trace [numCycles*25]
     *  per-cycle packed rows, mstats [numCycles*5] per-cycle measure counts, result =
     *  final packed [25], vectorOut = final anchor [3], prepOut = last full prepare [8],
     *  stats = last-cycle {pairs, surviving, misaligned} (each nullable). Peaks/corr
     *  indices stay resident for the unchanged fetch calls. Returns the LAST cycle's
     *  corr-row count (>0) or a negative (host -1xx / device guard codes).
     *  By Claude on 07/17/2026. */
    int     tp_proc_exec_pose_scene_dp(Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen,
                                      float[] poseVectors, int[] taskMap, float[] taskCenters,
                                      int numCycles, float lambda, float pureWeight, float rmsDiff,
                                      int numTiles, int corrSlot, int ntileShift,
                                      int numComponents, int useEigen,
                                      float eigMinSqrt, float eigMaxSqrt,
                                      float minConfidence, int sameWeights,
                                      int prepareLight,
                                      float[] centers, float[] pullTargets, float[] paramRegweights,
                                      float[] vector,
                                      float[] trace, int[] mstats, float[] result,
                                      float[] vectorOut, float[] prepOut, int[] stats);
    // ---- imagej ROADMAP item 4 (combined image + LoG) rung R1: the full-frame
    // combined virtual render as ONE async chain on a LOW-PRIORITY stream,
    // pipelined one scene behind the pose service. By Claude on 07/19/2026. ----
    /** Arm the render stage (re-callable per sequence): allocate render-private
     *  resources, snapshot the static camera cv + rot-derivs, latch the task/
     *  consolidate policy. Requires geometry already uploaded. 0 on success. */
    int     tp_proc_render_setup(Pointer proc,
                                      float disparityCorr, float minPx, float maxPx,
                                      float minPy, float maxPy, float soft, float hard);
    /** Per-sequence render template: packed txy map + reference px,py,disparity
     *  for every tile with valid disparity (count &le; full frame). */
    int     tp_proc_render_template(Pointer proc, int[] taskMap, float[] taskCenters, int ntiles);
    /** Render-private camera residents (ref group = per sequence, scene group =
     *  per scene); null metas skip a group. The pose loop's own residents move
     *  on to scene N+1 before render(N) executes - never shared. */
    int     tp_proc_render_cameras(Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen);
    /** Direct task-slot seed (tests / Java-centers A/B); bypasses the on-device
     *  task finalize. */
    int     tp_proc_render_set_tasks(Pointer proc, int slot, float[] ftasks, int ntiles, int totalFloats);
    /** NON-BLOCKING per-scene render: enqueue task-finalize (at the FITTED pose)
     *  -> geometry -> erase -> dual-MB convert -> 16-sensor consolidate -> cam0
     *  imclt on the render stream + record the done event. imgSet = the ring set
     *  scene N was conditioned into. 0 on success (enqueue only). */
    int     tp_proc_render_scene(Pointer proc, float[] poseVec, int vecFloats,
                                      float dxMain, float dyMain, float dxSub, float dySub,
                                      float scaleMain, float scaleSub,
                                      int taskCodeMain, int taskCodeSub,
                                      int imgSet, int useTaskUpdate);
    /** Completion probe: wait=0 -> 0 running / 1 done / &lt;0 error; wait=1 blocks.
     *  stats (int[4], may be null): done flag, pairs, surviving, misaligned. */
    int     tp_proc_render_status(Pointer proc, int wait, int[] stats);
    /** TEST-ONLY D2H of the rendered combined image (ruling 07/19 3.4): pitched
     *  (imgW+8) x (imgH+8) floats, de-pitched by the caller. 1 on success. */
    int     tp_proc_render_get(Pointer proc, float[] out);
    /** item-4 step-4: bind a FRESH render-only proc (its own - normally carved -
     *  module, NO tp_proc_setup) to the pose proc whose image ring and static
     *  residents (gc/rbr/kernels) the render chain reads cross-context. Call
     *  BEFORE tp_proc_render_setup on the render proc. 0 on success.
     *  By Claude on 07/19/2026. */
    int     tp_proc_render_bind_source(Pointer renderProc, Pointer srcProc);
    // ---- item-4 R2 (07/19/2026): pixel-LoG + post-LoG subtract-average on the
    // resident render frame, enqueued after render_dp on the render stream. ----
    /** Arm the LoG/subtract-avg stage (ALL allocations happen here - the
     *  standing no-alloc-in-loop rule). taps = (2*krad+1)^2 LoG taps; avgInit =
     *  post-LoG-domain base (prior-run average file LoG'd in Java double) of
     *  imgW*imgH floats or null (NaN start: EMA seeds from the first frame;
     *  emaK=0 keeps the diff NaN there). Re-call per sequence resets avg +
     *  accumulators (rerun == fresh). 0 on success. By Claude on 07/19/2026. */
    int     tp_proc_render_log_setup(Pointer proc, float[] taps, int krad,
                                      float[] avgInit, int avgLen,
                                      float emaK, float gateAbs, int accumEnable, int wantRaw);
    /** D2H of an R2 buffer (debug/eval + the end-of-run average save; never
     *  per-scene in production). which: 0=diff (detection frame), 1=avg
     *  (post-LoG), 2=raw LoG, 3=accum sum, 4=accum count. Blocking; out =
     *  imgW*imgH floats (sensor window, packed). 1 on success. */
    int     tp_proc_render_log_get(Pointer proc, int which, float[] out);

    // ---- D5 overlap (design ratified 2026-07-17): device image-set ring (D5a) +
    // launch/collect split of the scene entry (D5b). By Claude on 07/17/2026. ----
    /** Select (lazily allocating) device image set 'set' as the ACTIVE set - all image
     *  consumers and staged uploads follow it. Flip only AFTER launching the chain that
     *  consumes the previous set. 0 on success. */
    int     tp_proc_img_set_select(Pointer proc, int set);
    /** Record the active set's ready event on the copy stream ("uploads landed") - the
     *  CPU-conditioned fallback; the GPU path's async conditioning records it itself. */
    int     tp_proc_img_set_ready_record(Pointer proc);
    /** Async in-place conditioning of the ACTIVE image set on the copy stream (production
     *  float two-map kernel); records the set's ready event after the kernel - ONE event
     *  = uploaded+conditioned. No host synchronization. 0 on success. */
    int     tp_proc_exec_conditioning_async(Pointer proc);
    /** D5b launch half of tp_proc_exec_pose_scene_dp: same inputs, GPU-side stream-wait on
     *  the active set's ready event instead of the host fence, raw enqueue with no context
     *  sync - returns 1 immediately while the chain runs (ONE chain in flight;
     *  wantTrace/wantMstats fix what the later collect may read). */
    int     tp_proc_exec_pose_scene_dp_launch(Pointer proc,
                                      float[] refMeta, float[] refRadial,
                                      float[] refRbr, int refRbrLen, float[] refErs, int refErsLen,
                                      float[] sceneMeta, float[] sceneRadial,
                                      float[] sceneRbr, int sceneRbrLen, float[] sceneErs, int sceneErsLen,
                                      float[] poseVectors, int[] taskMap, float[] taskCenters,
                                      int numCycles, float lambda, float pureWeight, float rmsDiff,
                                      int numTiles, int corrSlot, int ntileShift,
                                      int numComponents, int useEigen,
                                      float eigMinSqrt, float eigMaxSqrt,
                                      float minConfidence, int sameWeights,
                                      int prepareLight,
                                      float[] centers, float[] pullTargets, float[] paramRegweights,
                                      float[] vector,
                                      int wantTrace, int wantMstats);
    /** D5b collect half: wait for the launched chain (implicitly - the first D2H) and fetch
     *  the end-of-scene results; returns the last cycle's corr-row count exactly like the
     *  blocking entry (or a negative). */
    int     tp_proc_exec_pose_scene_dp_collect(Pointer proc,
                                      float[] trace, int[] mstats, float[] result,
                                      float[] vectorOut, float[] prepOut, int[] stats);
    /** Deterministic double normal-equation products for the lean pose LMA.
     *  jt is parameter-major [numParams][numValues]; out is row-major H followed by b.
     *  Damping and the small solve remain in Java. 0 on success. */
    int     tp_proc_exec_lma_products(Pointer proc,
                                      double[] weights, double[] jt, double[] ymfxWeighted,
                                      int numParams, int numValues, double[] out);
    int     tp_proc_num_corr_tiles(Pointer proc);
    int     tp_proc_num_corr_combo(Pointer proc);
    /** Upload to a named __constant__ symbol (lpf_data / lpf_corr / lpf_rb_corr). 0 on success. */
    int     tp_proc_set_const(Pointer proc, String name, float[] data, int n);
    // ---- oracle TD-correlation readback / erase ----
    int     tp_proc_get_corr_indices(Pointer proc, int[] out, int n);
    int     tp_proc_get_corr_combo_indices(Pointer proc, int[] out, int n);
    int     tp_proc_get_corr_td(Pointer proc, float[] out);
    /** Upload host-built TD per-pair corr indices + data (TD->PD re-conversion path). 0 on success. */
    int     tp_proc_set_corr_indices_td(Pointer proc, int[] corr_indices, float[] fdata, int num_tiles);
    int     tp_proc_erase_corrs(Pointer proc);
    // ---- oracle textures: textures_nonoverlap (DP) + readback (matches GpuQuad.execTextures_DP) ----
    /** textures_nonoverlap<<<1,1>>>: CDP-builds the index list + accumulate. params5={min_shot,scale_shot,
     *  diff_sigma,diff_threshold,min_agree}; weights3=R,B,G. calc_textures->gpu_textures, calc_extra->
     *  diff_rgb_combo, linescan_order=0 in production. Returns the kernel-built texture-tile count. */
    int     tp_proc_exec_textures(Pointer proc, float[] params5, float[] weights3, int isLwir,
                                  int dustRemove, int keepWeights, int calcTextures, int calcExtra, int linescanOrder);
    /** Read min(count,n) packed texture indices into out; returns the true count (gpu_texture_indices_len). */
    int     tp_proc_get_texture_indices(Pointer proc, int[] out, int n);
    /** Read n = num_tex*num_cams*(num_colors+1) floats of diff_rgb_combo (texture_indices order). */
    int     tp_proc_get_diff_rgb_combo(Pointer proc, float[] out, int n);
    /** De-pitch gpu_textures into out[num_tiles * slices*256], slices per keep_weights. */
    int     tp_proc_get_textures(Pointer proc, float[] out, int numTiles, int numColors, int keepWeights);
    void    tp_proc_destroy(Pointer proc);
    /** Validate the persistent path: convert CLT (outCltErr), imclt RBG (outRbgErr), quad corr
     *  order-independent (outCorrErr), + no_kernels smoke (outNokernMax). */
    int     tp_proc_convert_selftest(Pointer module, int lwir, String data_root,
                                     double[] outCltErr, double[] outRbgErr, double[] outCorrErr,
                                     double[] outNokernMax, int[] outNumActive);
}
