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>
56 lines
1.7 KiB
C
56 lines
1.7 KiB
C
/* v0.4 smoke test publisher — NV12 1920x1080 ring 4, fill каждый slot
|
||
* с pattern (i % 256), publish, infinite loop. */
|
||
#include <cuframes/cuframes.h>
|
||
#include <cuda_runtime.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
#include <unistd.h>
|
||
|
||
int main(int argc, char **argv) {
|
||
const char *key = argc > 1 ? argv[1] : "smoke";
|
||
|
||
cuframes_publisher_config_t cfg = {0};
|
||
cfg.key = key;
|
||
cfg.width = 1920;
|
||
cfg.height = 1080;
|
||
cfg.format = CUFRAMES_FORMAT_NV12;
|
||
cfg.ownership = CUFRAMES_OWNERSHIP_LIBRARY;
|
||
cfg.ring_size = 4;
|
||
cfg.policy = CUFRAMES_POLICY_DROP_OLDEST;
|
||
cfg.cuda_device = 0;
|
||
|
||
cuframes_publisher_t *pub = NULL;
|
||
int r = cuframes_publisher_create(&cfg, &pub);
|
||
if (r != CUFRAMES_OK) {
|
||
fprintf(stderr, "publisher create failed: %d (%s)\n", r, cuframes_strerror(r));
|
||
return 1;
|
||
}
|
||
fprintf(stderr, "publisher 'cuframes-%s' ready (v0.4 VMM)\n", key);
|
||
|
||
cudaStream_t stream;
|
||
cudaStreamCreate(&stream);
|
||
|
||
int i = 0;
|
||
while (1) {
|
||
void *ptr = NULL;
|
||
r = cuframes_publisher_acquire(pub, &ptr);
|
||
if (r != CUFRAMES_OK) { fprintf(stderr, "acquire: %d\n", r); break; }
|
||
|
||
uint8_t pattern = (uint8_t)(i & 0xFF);
|
||
cudaMemsetAsync(ptr, pattern, 1920 * 1080 * 3 / 2, stream);
|
||
|
||
r = cuframes_publisher_publish(pub, stream,
|
||
(int64_t)cuframes_now_ns());
|
||
if (r != CUFRAMES_OK) { fprintf(stderr, "publish: %d\n", r); break; }
|
||
i++;
|
||
if (i % 50 == 0) fprintf(stderr, "published %d frames\n", i);
|
||
struct timespec ts = {.tv_sec = 0, .tv_nsec = 40000000}; /* 25 fps */
|
||
nanosleep(&ts, NULL);
|
||
}
|
||
|
||
cudaStreamDestroy(stream);
|
||
cuframes_publisher_destroy(pub);
|
||
return 0;
|
||
}
|