Files
cuframes-composer/include/cuframes_composer/cpp/source_pool.hpp
T
gx beb8e1baa0 Phase 11b B-E: ООП-гипотеза проверена end-to-end
Что в этом коммите:

Decoration реализации:
  - cpp/label_decoration.hpp/.cpp — FreeType atlas + cugrid_blit_rgba_nv12.
    UTF-8 декодер, atlas в VRAM (RAII через CudaBuffer), rebuild при set_text.
  - cpp/border_decoration.hpp/.cpp — 4 cugrid_fill_nv12 (top/bottom/left/right).

Cell реализации:
  - cpp/camera_cell.hpp/.cpp — cfc_source_get_latest + cugrid_resize_nv12.
    Non-owning указатель на cfc_source_t (pool владеет).
  - cpp/widget_cell.hpp/.cpp — тёмный fill placeholder.
  - cpp/blank_cell.hpp/.cpp — BT.709 black fill.

Layout и Template:
  - cpp/template.hpp — LayoutTemplate { name, cells[], priority }.
    8×8 микро-сетка (kGridCols=kGridRows=8). to_pixels() переводит в Rect.
  - cpp/layout.hpp/.cpp — vector<unique_ptr<Cell>>, apply() создаёт
    CameraCell/WidgetCell/BlankCell + Decorations (Label с "{key} prio={N}").
  - cpp/template_loader.hpp/.cpp — JSON → vector<LayoutTemplate> через json-c.
    builtin_templates() = { tpl_1, tpl_4 } как fallback.

SourcePool:
  - cpp/source_pool.hpp/.cpp — owner cfc_source_t*, motion state атомарный,
    zone-filter в motion_pulse. Pool entries — non-copyable unique_ptr.

Composer:
  - cpp/composer.hpp/.cpp — owner SourcePool + templates + Layout + output.
    Алгоритмы: pick_best_fit (min nb_camera_cells >= need + priority tie-break),
    collect_active (drawable AND motion_within_TTL), asymmetric hysteresis
    (рост сразу через std::includes, сжатие — wait shrink_hysteresis_ms).
    Public C++ API: set_motion_mode / set_layout / load_templates / compose_frame.

ООП-гипотеза smoke:
  - examples/grid_record_cpp.cpp — минимальный smoke без NVENC. Init composer,
    compose_frame N раз, dump NV12 в файл. Проверяет что C++ модель
    компилируется, линкуется с C-кодом (source.c, nvenc.c остались на C через
    extern "C"), и реально рисует кадр.

Производительность сохранена:
  - Один output буфер VMM, передаётся как NV12Ref (read-write reference) во все
    cells/decorations — НИКАКИХ memcpy на cells boundary.
  - Virtual call overhead: 1 indirect call per cell per frame. Negligible.
  - Heap allocations только при apply_template (раз в N секунд при relayout).

Build:
  - CMakeLists.txt: CXX language, C++17.
  - src/CMakeLists.txt: COMPOSER_SOURCES_CPP добавлен в lib.
  - examples/CMakeLists.txt: grid_record_cpp.

Smoke test run jammy:
  [cfc/loader] docker/templates.json: loaded 7 templates
  [smoke] composer 1920x1080 templates=7 sources=0 motion=0
  [smoke] wrote 3317760 bytes (Y=2211840 UV=1105920) to /out/blank.nv12
  Build PASS, init PASS, compose PASS, dump PASS.

Что НЕ сделано:
  - extern "C" ABI shim для control.c / grid_record.c (старый C-композитор
    всё ещё единственный для prod stack).
  - Удаление старых composer.c / overlay.c / layouts.c.
  - Live deploy в прод (Step 1-3 функциональность).
  - JSON ZMQ hot-reload (был в Step 3 C-version, восстановить в C++).

Refs: #195 (Phase 11b C++ refactor).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 21:43:18 +01:00

96 lines
3.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* SourcePool — пул cuframes-источников композитора (Phase 11b).
*
* Каждая запись: cuframes_key + frigate_camera + priority + cfc_source_t* +
* motion state (last_motion_ms, zone-filter). Pool создаётся при старте
* композитора (через add() вызовы) и живёт всю сессию.
*
* Cells (CameraCell) держат non-owning указатели на cfc_source_t — pool
* владеет.
*
* Лицензия: LGPL-2.1+
*/
#ifndef CUFRAMES_COMPOSER_CPP_SOURCE_POOL_HPP
#define CUFRAMES_COMPOSER_CPP_SOURCE_POOL_HPP
#include "../source.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
namespace cfc {
struct PoolEntry {
std::string cuframes_key;
std::string frigate_camera;
int priority = 0;
cfc_source_t* source = nullptr;
std::atomic<std::int64_t> last_motion_ms{0};
std::vector<std::string> required_zones;
/* Получить snapshot для drawable-checks без локов. */
cfc_source_state_t state() const {
if (!source) return CFC_SOURCE_DISCONNECTED;
cfc_source_snapshot_t s{};
cfc_source_get_latest(source, &s);
return s.state;
}
bool drawable() const {
cfc_source_state_t st = state();
return st == CFC_SOURCE_ACTIVE || st == CFC_SOURCE_STALE;
}
};
class SourcePool {
public:
SourcePool() = default;
~SourcePool();
SourcePool(const SourcePool&) = delete;
SourcePool& operator=(const SourcePool&) = delete;
/* Параметры подписки cuframes (default per cfc_source_config_t). */
struct SubscribeOpts {
int cuda_device = 0;
std::string consumer_prefix = "composer";
int reconnect_min_ms = 1000;
int reconnect_max_ms = 30000;
int stale_threshold_ms = 500;
int dead_threshold_ms = 5000;
};
/* Добавить источник в pool. Возвращает индекс или -1. */
int add(const std::string& cuframes_key,
const std::string& frigate_camera,
int priority,
const std::vector<std::string>& zones,
const SubscribeOpts& opts);
int size() const { return static_cast<int>(entries_.size()); }
PoolEntry* by_index(int i) { return i >= 0 && i < size() ? entries_[i].get() : nullptr; }
PoolEntry* by_key(const std::string& key);
/* Уведомить о motion (вызывается из Frigate MQTT subscriber'а через
* C-shim). Если zone-filter задан — проверяет пересечение. */
void motion_pulse(const std::string& frigate_camera,
const std::vector<std::string>& current_zones);
/* Итерация (для best-fit selection и health). */
template <typename F>
void for_each(F&& fn) {
for (auto& e : entries_) fn(*e);
}
private:
std::vector<std::unique_ptr<PoolEntry>> entries_;
std::mutex mu_;
};
} // namespace cfc
#endif /* CUFRAMES_COMPOSER_CPP_SOURCE_POOL_HPP */