makepad/libs/ai/cuda/kernels/pixal.cu
Admin 60615db4ed libs: ai hub/models/cuda, speech, chat_ui
Squash of 54 work commits (Sep 1–12):
  6251f7c  ai-hub: body domain — live pose packets ride the realtime session
  ea50c77  chat_ui: the feed's session gets its profile brief back
  f51b5f3  ai-body: the crate for the native SAM 3D Body port, with its weights reader
  8211ae6  ai-body: the MHR rig and the pose head's parameter decoding, oracle-exact
  9e343a8  ai-body: the DINOv3 ViT-H+/16 backbone, crop and ray conditioning; Metal gains rope-half and affine layer norm
  69d842c  ai-body: the promptable pose decoder and its refinement loop, oracle-matched on Metal
  66e5e2f  ai-hub: SAM 3D Body runs natively — `sam3dbody` on the body domain, oracle-matched end to end
  a634198  ai-hub: the body-native commit carried a peer's in-flight hub hunks; put them back where they were
  9ff44e8  ai-hub: the body-native wiring, this time only the lane's hunks
  6a1c16b  ai-body: third-party notices — what the port is implemented after, and what it is not
  d78411a  ai-body: the per-step work moves to the GPU
  b22259b  ai-body: the context stays on the GPU; only the pose token leaves the loop
  346f31f  ai-body: flash attention for the head-dim-64 blocks
  45b5b98  ai-body: the crop size is a runtime knob, and the loop reports where its time goes
  4be6d19  ai-body: the test modules import the grid constants they still use
  7598346  ai-body: tensor-core GEMMs for the backbone, and the rig's correctives only where they count
  a9ce596  ai-body: the crop warp runs across cores
  8964ba6  ai-body: an FP8 backbone mode, off by default, measured against the oracle
  a2aaa8f  ai-body: the FP8 bias rides a column-broadcast add on the device
  d53c77d  metal: a device-resident ViT stack, and the body backbone rides it
  d006d0a  metal: resident f32 linears keep their weight on the device
  525ba1c  metal: a device-resident two-way decoder layer, and the body decoder rides it
  c9e6d88  ai-body: the hands pass — hand crops, the hand decoder, the hand-mode rig and the wrist fusion
  62dff26  ai-body: the mask prompt — a person's segmentation mask conditions the body pass
  a648cf8  ai-hub: body session options — hands, detect, persons=N
  8c568df  ai-hub: drop the SAM 3D Body reference worker backend
  7ff875a  ai-hub: keep a peer's in-flight beats/notes/local work out of the body commits
  31e5faa  ai-hub: local model runner, licence acknowledgements, a shared install panel; Beat This!, Basic Pitch and the Salamander drum-kit entries
  b94bc58  ai-services: the wire, the app port and the panel state — one conversation, many apps
  2acb798  ai-services: wire v2 — endpoints, receiver-side caps, result disposition
  8ae0ffb  ai-services: the engine core — registry, router and conversation, tested against a scripted model
  2308736  ai-services: the real models behind the engine feature — local through the hub, Claude, and none
  c3f631d  livepipe: one reusable pipe from a camera to a fleet node and back
  ff62db3  ai libs: the runtime env-var cleanup — precision is a per-caller policy, not an environment side channel
  04a94ef  realtime: one service-log line when a live session opens and one when it closes
  0ecb81c  ai models: the model-crates env-var cleanup — 172 research knobs gone, the unset default is the code
  4ca36c1  ai hub + services: the assistant's model comes from wherever it is resident — the fleet chat box, with tools, then the local weights
  432121e  aichat engine + wm: launch, then use — the assistant continues in the same turn once the app it started is on the bus
  7a5bf69  ai-hub registry: the Salamander drumkit samples come from the makepad.nl mirror — the GitHub repo only carries the .sfz files
  102ffc5  ai-services: messages on the bus — a manifest declares topics, the engine subscribes on a tool's behalf or by ToolResult.subscribe, a service publishes Message frames, an idle conversation wakes on a message as an event turn under rate laws; the WM bus forwards the new frames; every app that matches the wire gets its arm
  a837792  hub + flow: a whitespace-only chat completion is retried once and then fails instead of passing as an answer; a flow's model is a fleet model id unless it names a weight file on disk; chat models show under the text domain in /v1/models
  bc6c620  hub + flow: what the chat review found — the in-process route retries an empty completion too, a node says whether its prefill opened thinking so a brief-mode answer is never discarded, a preferred model falls back to normal election when no node has it, discovery keeps looking for the preferred model until patience runs out
  75c3441  hub: the PRO 6000 serves image as well as chat and text
  ad5e98b  hub registry: flux2-dev's VRAM estimate is its measured peak, 30 GB
  c7241e0  hub: a node that evicted every resident releases its cached allocator pool before refusing a load or publishing usable VRAM
  30575f0  flow: route generation by request workload
  1be1e21  ai-hub: gate downloads by disk capacity and recover fleet admission
  df6b394  filesystem_watcher, bounded_http, ai services: live and tool prerequisites
  79ebdb9  ai-hub: add a native Pixal3D image-to-3D backend
  0ba0d74  ai-hub: propagate typed refusals under reject queue policy
  cc6c872  Speed up H3 conditioning and video decoding
  e512059  Fix Qwen vision residency and generated material colors
  2864f68  ai-hub http client: bound every plain TCP connect to 3 s per address
  3d93229  ai: CUDA is a Linux/Windows-only dependency; the hub library defaults to llm + stt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:31 +02:00

133 lines
6.1 KiB
Text

// Native sparse Pixal3D feature conditioning. See the trellis model crate's
// THIRD_PARTY_NOTICES.md. No NATTEN, unfold, or dense upsampled V allocation.
#include <cuda_runtime.h>
#include <stdint.h>
#include <float.h>
#include <math.h>
static __global__ void pixal_naf_sample_kernel(
const float* __restrict__ q, const float* __restrict__ k,
const float* __restrict__ v, const float* __restrict__ uv,
float* __restrict__ out, uint32_t width, uint32_t height,
uint32_t low_width, uint32_t low_height, uint32_t heads,
uint32_t qc, uint32_t vc, uint32_t kernel) {
const uint32_t token = blockIdx.x, head = blockIdx.y;
const uint32_t qd = qc / heads, vd = vc / heads;
const uint32_t kk = kernel * kernel;
const int dx = width / low_width, dy = height / low_height;
const int radius = kernel / 2;
const float x = fminf(fmaxf(uv[2*token] * width - 0.5f, 0.0f), float(width-1));
const float y = fminf(fmaxf(uv[2*token+1] * height - 0.5f, 0.0f), float(height-1));
const int x0 = int(floorf(x)), y0 = int(floorf(y));
const float tx = x-x0, ty = y-y0;
// Up to 15x15; a single block owns one voxel/head and four sample pixels.
extern __shared__ float scratch[];
float* scores = scratch;
int* indices = reinterpret_cast<int*>(scores + 4*kk);
for (uint32_t i = threadIdx.x; i < 4*kk; i += blockDim.x) {
const uint32_t corner = i / kk, tap = i % kk;
const int px = min(x0 + int(corner & 1), int(width)-1);
const int py = min(y0 + int(corner >> 1), int(height)-1);
const int nx = px + (int(tap % kernel)-radius)*dx;
const int ny = py + (int(tap / kernel)-radius)*dy;
const bool valid = nx >= 0 && ny >= 0 && nx < int(width) && ny < int(height);
const int p = valid ? int((ny/dy)*low_width + nx/dx) : -1;
indices[i] = p;
float sum = 0.0f;
if (valid) {
const size_t qi = (size_t(py)*width+px)*qc + head*qd;
const size_t ki = size_t(p)*qc + head*qd;
for (uint32_t c=0; c<qd; ++c) sum += q[qi+c]*k[ki+c];
}
scores[i] = sum * rsqrtf(float(qd));
}
__syncthreads();
if (threadIdx.x < 4) {
const uint32_t corner = threadIdx.x;
float* s = scores + corner*kk;
float maximum = -FLT_MAX;
for (uint32_t i=0; i<kk; ++i) maximum = fmaxf(maximum,s[i]);
float sum=0.0f;
for (uint32_t i=0; i<kk; ++i) { s[i]=expf(s[i]-maximum); sum+=s[i]; }
const float weight = ((corner&1)?tx:1.0f-tx)*((corner&2)?ty:1.0f-ty);
for (uint32_t i=0; i<kk; ++i) s[i] *= weight/sum;
}
__syncthreads();
for (uint32_t c=threadIdx.x; c<vd; c+=blockDim.x) {
float value=0.0f;
for (uint32_t i=0; i<4*kk; ++i) {
const int p=indices[i];
if (p>=0) value += scores[i]*v[size_t(p)*vc+head*vd+c];
}
out[size_t(token)*vc+head*vd+c]=value;
}
}
extern "C" cudaError_t makepad_cuda_pixal_naf_sample_f32(
const float* q, const float* k, const float* v, const float* uv, float* out,
uint32_t count, uint32_t width, uint32_t height,
uint32_t low_width, uint32_t low_height, uint32_t heads,
uint32_t qc, uint32_t vc, uint32_t kernel, cudaStream_t stream) {
if (!count) return cudaSuccess;
const size_t shared = 4*kernel*kernel*(sizeof(float)+sizeof(int));
pixal_naf_sample_kernel<<<dim3(count,heads),256,shared,stream>>>(
q,k,v,uv,out,width,height,low_width,low_height,heads,qc,vc,kernel);
return cudaGetLastError();
}
// Planar adaptive average pooling. Pool before transposing and applying
// positional embeddings; all spatial reductions remain on device.
static __global__ void pixal_pool_kernel(const float* x, float* out,
uint32_t width, uint32_t height, uint32_t ow, uint32_t oh, uint32_t channels) {
const size_t i = size_t(blockIdx.x)*blockDim.x+threadIdx.x;
const size_t plane = size_t(ow)*oh;
if (i >= plane*channels) return;
const uint32_t c=i/plane, px=i%ow, py=(i%plane)/ow;
const uint32_t x0=px*width/ow, x1=((px+1)*width+ow-1)/ow;
const uint32_t y0=py*height/oh, y1=((py+1)*height+oh-1)/oh;
float sum=0.0f;
for(uint32_t y=y0;y<y1;++y) for(uint32_t xx=x0;xx<x1;++xx)
sum+=x[(size_t(c)*height+y)*width+xx];
out[i]=sum/float((x1-x0)*(y1-y0));
}
extern "C" cudaError_t makepad_cuda_pixal_pool_f32(const float* x, float* out,
uint32_t width, uint32_t height, uint32_t ow, uint32_t oh, uint32_t channels,
cudaStream_t stream) {
const size_t n=size_t(ow)*oh*channels;
if(!n) return cudaSuccess;
pixal_pool_kernel<<<(n+255)/256,256,0,stream>>>(x,out,width,height,ow,oh,channels);
return cudaGetLastError();
}
// Pooling produces planar 256-channel features. Fuse their transpose and
// four-head 2D RoPE, computing each phase once per pixel/pair on the GPU.
// This replaces two 128 MiB host phase tables at the 1024 guide resolution.
static __global__ void pixal_rope_kernel(const float* x, const float* periods,
float* out, uint32_t width, uint32_t height) {
const size_t i=size_t(blockIdx.x)*blockDim.x+threadIdx.x;
const size_t pixels=size_t(width)*height;
if(i>=pixels*32) return;
const size_t pixel=i/32;
const uint32_t pair=i%32;
const float coord=pair<16 ? float(pixel/width) : float(pixel%width);
const float side=pair<16 ? float(height) : float(width);
const float p=2.0f*(coord+0.5f)/side-1.0f;
float sine,cosine;
__sincosf(6.283185307179586f*p/periods[pair%16],&sine,&cosine);
for(uint32_t head=0;head<4;++head) {
const uint32_t channel=head*64+pair;
const float a=x[size_t(channel)*pixels+pixel];
const float b=x[size_t(channel+32)*pixels+pixel];
out[pixel*256+channel]=a*cosine-b*sine;
out[pixel*256+channel+32]=b*cosine+a*sine;
}
}
extern "C" cudaError_t makepad_cuda_pixal_rope_f32(const float* x, const float* periods,
float* out, uint32_t width, uint32_t height, cudaStream_t stream) {
const size_t count=size_t(width)*height*32;
if(!count) return cudaSuccess;
pixal_rope_kernel<<<(count+255)/256,256,0,stream>>>(x,periods,out,width,height);
return cudaGetLastError();
}