6.8 KiB
title, sidebar_position
| title | sidebar_position |
|---|---|
| Ownership modes | 2 |
Ownership modes
Кто владеет CUDA-памятью в которую publisher пишет frame'ы — сама библиотека или внешний код. В заголовке cuframes.h объявлены оба варианта:
typedef enum cuframes_ownership_mode {
CUFRAMES_OWNERSHIP_LIBRARY = 0,
CUFRAMES_OWNERSHIP_EXTERNAL = 1,
} cuframes_ownership_mode_t;
Но в v0.4 работает только LIBRARY. EXTERNAL оставлен в API для бинарной совместимости и помечен deprecated. Ниже — почему и что с этим делать если ваш код раньше использовал EXTERNAL.
LIBRARY mode (единственный рабочий в v0.4)
Publisher просит библиотеку аллоцировать ring заданного размера. Каждый кадр publisher получает чистый slot, пишет в него, отдаёт обратно через publish.
cuframes_publisher_config_t cfg = {
.key = "cam1",
.width = 1920,
.height = 1080,
.format = CUFRAMES_FORMAT_NV12,
.ownership = CUFRAMES_OWNERSHIP_LIBRARY,
.ring_size = 4,
.policy = CUFRAMES_POLICY_DROP_OLDEST,
};
cuframes_publisher_t *pub;
cuframes_publisher_create(&cfg, &pub);
void *slot;
cuframes_publisher_acquire(pub, &slot);
// NVDEC / cuMemcpy / kernel пишет в slot
cuframes_publisher_publish(pub, stream, cuframes_now_ns());
Под капотом библиотека:
- Аллоцирует
ring_sizeслотов черезcuMemCreate(CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR). cuMemMap+cuMemSetAccessчтобы локальный publisher мог писать.- При subscribe передаёт POSIX FD через
sendmsg(SCM_RIGHTS). - Subscriber делает
cuMemImportFromShareableHandle— получает указатель на ту же физическую HBM.
Этот путь zero-copy для consumer'ов. На publisher'е memory overhead = ring_size × frame_size. На consumer'е — ноль.
EXTERNAL mode (deprecated в v0.4)
Идея была: publisher уже имеет CUDA pointer'ы из чужого pool'а (FFmpeg AVHWFramesContext, NVDEC output, DeepStream, какой-то custom decoder) и хочет их просто пошарить, без extra allocation и без D2D copy.
// Так раньше работало в v0.1–0.3. В v0.4 не работает.
cuframes_publisher_create_external(&cfg, ffmpeg_pool_ptrs,
pool_size, frame_size, &pub);
В v0.4 этот путь сломан by design.
Почему EXTERNAL не работает с VMM
cuframes v0.4 публикует frame'ы через POSIX FD, потому что это единственный CUDA-IPC канал который не требует shared PID namespace (см. memory note про v0.4 и sync writeup). FD получается через cuMemExportToShareableHandle — а эта функция требует чтобы память была аллоцирована через cuMemCreate с соответствующим requestedHandleType.
Существующий cudaMalloc / cudaMallocPitch pointer (тот что отдаёт FFmpeg или DeepStream) к VMM не относится. Экспортировать его как POSIX FD нечем. Старый путь v0.3 использовал cudaIpcGetMemHandle (opaque 64-байтовая структура, передавалась через socket payload) — он работал с любой памятью, но требовал shared PID. На v0.4 от него ушли осознанно.
Что делать вместо
Если у вас уже есть GPU pool из FFmpeg / NVDEC / etc — переходите на LIBRARY mode с одним extra device-to-device copy:
// FFmpeg выдаёт frame в hwframe pool:
AVFrame *src = ...; // src->data[0] = cudaMalloc'd by FFmpeg
void *slot;
cuframes_publisher_acquire(pub, &slot);
// 1 × DtoD copy с pitch:
cuMemcpy2DAsync(&(CUDA_MEMCPY2D){
.srcMemoryType = CU_MEMORYTYPE_DEVICE,
.srcDevice = (CUdeviceptr)src->data[0],
.srcPitch = src->linesize[0],
.dstMemoryType = CU_MEMORYTYPE_DEVICE,
.dstDevice = (CUdeviceptr)slot,
.dstPitch = pub_pitch,
.WidthInBytes = width,
.Height = height_y + height_uv,
}, stream);
cuframes_publisher_publish(pub, stream, pts_ns);
Так переведён инструмент cuframes-rtsp-source в составе репозитория cuframes — раньше он принимал FFmpeg pool через EXTERNAL, теперь делает acquire + 1 D2D copy. Overhead — единичный DtoD на 1920×1080 NV12 это десятки микросекунд, в порядке шума на фоне cuStreamSynchronize.
Memory trade-off
| LIBRARY (v0.4) | EXTERNAL (v0.3, deprecated) | |
|---|---|---|
| Publisher extra alloc | ring_size × frame_size |
0 |
| D2D copy per frame | 1 (если есть upstream pool) или 0 (если decoder пишет прямо в slot) | 0 |
| Zero-copy для consumers | да | да |
| Работает без shared PID | да | нет |
| Поддерживается в v0.4 | да | нет |
Если decoder можно научить писать прямо в slot (acquire сначала, потом decode в полученный pointer) — extra D2D исчезает. Так делает cuframes-rtsp-source со своим NVDEC pipeline'ом.
Вернётся ли EXTERNAL
Если NVIDIA добавит способ экспорта cudaMalloc-памяти как POSIX FD — да, это вернёт zero-D2D путь без жертвования cross-namespace. На момент CUDA 12.4 такого API нет, и в roadmap NVIDIA это не анонсировано. На практике рассчитывать на это не стоит.
Поле ownership в cuframes_publisher_config_t остаётся ради ABI стабильности. Передача CUFRAMES_OWNERSHIP_EXTERNAL в v0.4 вернёт CUFRAMES_ERR_INVALID_ARG. Вызов cuframes_publisher_create_external объявлен в заголовке, но возвращает ту же ошибку.
Следующее
- Synchronization — почему v0.4 ушёл от CUDA events и почему это связано с тем же VMM-ограничением.
- First publisher — рабочий LIBRARY-mode пример.
- Protocol reference — wire format VMM_FDS handshake.