4862247fe2
Заменяет cudaMalloc + cudaIpcGetMemHandle на cuMemCreate (VMM) +
cuMemExportToShareableHandle(POSIX_FILE_DESCRIPTOR). FDs передаются consumer'у
через sendmsg(SCM_RIGHTS) в handshake. Frigate (s6-overlay не даёт share PID)
и любой другой consumer работают БЕЗ pid namespace share — только volume mount
unix socket'a /run/cuframes и IPC share для /dev/shm header.
Sync: cudaEventRecord+IPC events → cuStreamSynchronize в do_publish.
Producer ждёт ~1 ms что stream flush'нулся, потом atomic_store(seq).
Consumer читает seq через memory_order_acquire и копирует DtoD без
event wait — HW coherence гарантирована на одном GPU.
ABI break (согласован с user'ом):
- magic 0xCC7C1DCC → 0xCC7C1DCE (старые consumers fail cleanly)
- protocol V3 → V4
- libcuframes.so.0 SOVERSION остаётся, но .so.0.3.0 → .so.0.4.0
- EXTERNAL ownership убран (VMM требует cuMemCreate-allocated memory,
нельзя export'нуть произвольный cudaMalloc-pointer как POSIX FD)
- cuframes-rtsp-source переведён на LIBRARY mode + один D2D memcpy
в acquire'нутый slot (overhead малый — публишер всё равно делал такой
D2D из FFmpeg hwframe pool в EXTERNAL pool раньше)
Размер: granularity 2 MB на 5090 → NV12 1920×1080 (~3.1 MB) округляется до
4 MB, +1 MB на slot × 16 × 4 камеры = +64 MB VRAM. Терпимо.
Packet ring (cuframes_packets://) НЕ затронут — отдельный SHM с своим
magic, работает как раньше.
PoC + smoke в spike/:
- vmm_fd_pingpong/ — minimal cuMemCreate+FD round-trip
- smoke_v04/ — full publisher+subscriber, 100/100 frames без pid share
Base image: Dockerfile.runtime → CUDA 12.4 (был 13.0). Matching prod
pipeline + Frigate base, иначе libcudart conflict при load.
Compose stack (localhost-infra repo) — параллельный commit:
- убран pid: container:cuframes-pub-parking из subscribers
- image теги: gx/cuframes:0.4, gx/cuda-grid-pipeline:phase8,
gx/frigate:cuframes-v0.4
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
64 lines
2.1 KiB
C
64 lines
2.1 KiB
C
/* v0.4 smoke subscriber — connect, read 100 frames, verify pattern, exit 0/1. */
|
|
#include <cuframes/cuframes.h>
|
|
#include <cuda_runtime.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
int main(int argc, char **argv) {
|
|
const char *key = argc > 1 ? argv[1] : "smoke";
|
|
|
|
cuframes_subscriber_config_t cfg = {0};
|
|
cfg.key = key;
|
|
cfg.consumer_name = "smoke-sub";
|
|
cfg.mode = CUFRAMES_MODE_NEWEST_ONLY;
|
|
cfg.cuda_device = 0;
|
|
cfg.connect_timeout_ms = 10000;
|
|
|
|
cuframes_subscriber_t *sub = NULL;
|
|
int r = cuframes_subscriber_create(&cfg, &sub);
|
|
if (r != CUFRAMES_OK) {
|
|
fprintf(stderr, "subscriber create failed: %d (%s)\n", r, cuframes_strerror(r));
|
|
return 1;
|
|
}
|
|
fprintf(stderr, "subscribed to '%s' (v0.4)\n", key);
|
|
|
|
cudaStream_t stream;
|
|
cudaStreamCreate(&stream);
|
|
size_t check_size = 1024; /* sample 1KB чтобы не тратить время */
|
|
uint8_t *host = malloc(check_size);
|
|
|
|
int frames = 0;
|
|
int good = 0;
|
|
while (frames < 100) {
|
|
cuframes_frame_t *f = NULL;
|
|
r = cuframes_subscriber_next(sub, stream, &f, 2000);
|
|
if (r != CUFRAMES_OK) {
|
|
fprintf(stderr, "next failed: %d (%s)\n", r, cuframes_strerror(r));
|
|
break;
|
|
}
|
|
cudaMemcpyAsync(host, cuframes_frame_cuda_ptr(f), check_size,
|
|
cudaMemcpyDeviceToHost, stream);
|
|
cudaStreamSynchronize(stream);
|
|
uint8_t exp = host[0];
|
|
int mismatch = 0;
|
|
for (size_t i = 1; i < check_size; i++) {
|
|
if (host[i] != exp) { mismatch++; }
|
|
}
|
|
if (mismatch == 0) good++;
|
|
if (frames % 20 == 0) {
|
|
fprintf(stderr, "frame seq=%lu byte0=0x%02x mismatch=%d\n",
|
|
(unsigned long)cuframes_frame_seq(f), exp, mismatch);
|
|
}
|
|
cuframes_subscriber_release(sub, f);
|
|
frames++;
|
|
}
|
|
free(host);
|
|
cudaStreamDestroy(stream);
|
|
cuframes_subscriber_destroy(sub);
|
|
|
|
fprintf(stderr, "DONE: %d/%d frames OK\n", good, frames);
|
|
return (good == frames && frames > 0) ? 0 : 1;
|
|
}
|