diff --git a/src/overlay.c b/src/overlay.c index 69c3e77..8f12192 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -643,7 +643,6 @@ static void detbox_rebuild_label_atlas(detbox_data_t *d, int slot) int w = 0, h = 0, ascent = 0; if (text_measure(face, txt, &w, &h, &ascent) != 0) return; if (w <= 0 || h <= 0) return; - unsigned char *cpu = calloc((size_t)w * h, 4); if (!cpu) return; /* Белый текст для контраста с pill background. */ @@ -811,11 +810,9 @@ int cfc_overlay_detbox_upsert(cfc_overlay_t *ov, const char *event_id, d->entries[slot].y2 = y2; d->entries[slot].last_update_ms = now_ms(); - /* Rebuild label atlas (если label/score изменились). FT render + CUDA - * upload под mutex'ом — это slow (~few ms), но upsert редкий (cooldown - * детектора 3s × 4 камеры × 2-3 labels ~ 3-4/sec total). Если text - * не изменился — no-op. */ - detbox_rebuild_label_atlas(d, slot); + /* НЕ rebuild в upsert: upsert вызывается из MQTT thread который не + * имеет CUDA context (cuMemAlloc → ERR_INVALID_CONTEXT err=201). Atlas + * lazily rebuilt в draw из main composer thread. */ pthread_mutex_unlock(&d->mu); return 0; @@ -866,6 +863,10 @@ static int draw_detection_boxes(cfc_overlay_t *ov, for (int i = 0; i < CFC_DETBOX_MAX; i++) { if (!d->entries[i].event_id[0]) continue; if (d->entries[i].last_update_ms < cutoff) continue; /* TTL expired */ + /* Lazy rebuild label atlas — выполняется в main composer thread + * где CUDA context активен (upsert thread его не имеет). No-op если + * label/score не изменились. */ + detbox_rebuild_label_atlas(d, i); snap[snap_n].x1 = d->entries[i].x1; snap[snap_n].y1 = d->entries[i].y1; snap[snap_n].x2 = d->entries[i].x2;