// crates/ml/src/cuda_pipeline/h_s2_p99_kernel.cu
//
// SP4 Layer A Task A9 — h_s2_p99 producer for ISV[H_S2_BOUND=169].
//
// Single-block, 256-thread kernel that `#include "sp4_histogram_p99.cuh"`
// directly (Task A4 header), reads `save_h_s2 [B × SH2]` (the trunk-output
// activation surface, populated each step by the online forward), computes
// p99(|save_h_s2|) via `sp4_histogram_p99<256>`, and writes the per-step
// observation to `producer_step_scratch_buf[scratch_idx]` with
// `__threadfence_system()` so the host pinned-mapped read sees the write.
//
// NB: distinct from the existing `h_s2_rms_ema_update` producer (slot 96)
// which tracks RMS (a different signal). Task A13 retrofitted that existing
// kernel to also use Pearls A+D, in a separate commit.
//
// The host launcher (`GpuDqnTrainer::launch_sp4_h_s2_p99`) follows up with
// the GPU `apply_pearls_ad_kernel` on the same stream which writes the new
// x_mean to ISV[H_S2_BOUND_INDEX=169] + Wiener state at slot 39 of
// `producer_step_scratch_buf` (Wiener offset `(169 - SP4_SLOT_BASE) * 3 = 114`).
// 2026-05-01 GPU-Pearls refactor — graph-capture-compatible (no host sync).

#include "sp4_histogram_p99.cuh"

extern "C" __global__ void h_s2_p99_update(
    const float* __restrict__ buf,
    int          count,
    float*       __restrict__ scratch_buf,
    int          scratch_idx
) {
    if (blockIdx.x != 0) return;
    float p99 = sp4_histogram_p99<256>(buf, count);
    if (threadIdx.x == 0) {
        scratch_buf[scratch_idx] = p99;
        __threadfence_system();   // make host-mapped write visible
    }
}
