加上纯色websocket

This commit is contained in:
2026-08-26 18:06:48 +08:00
parent 272d85caac
commit 2de8a35fff
28 changed files with 2061 additions and 549 deletions
+5 -13
View File
@@ -59,31 +59,23 @@ bool Frame_Pacing_Policy::accept_periodic_tick(double time_milliseconds) {
return false;
if (skip_next_periodic_.exchange(false, std::memory_order_acq_rel))
return false;
return !in_flight_.load(std::memory_order_acquire);
return true;
}
bool Frame_Pacing_Policy::request_immediate() {
const auto value = read();
if (!value.render_enabled) return false;
/* immediate 与下一次周期机会合并,真正的 busy/latest 语义由 Plot 管理。 */
skip_next_periodic_.store(true, std::memory_order_release);
if (in_flight_.load(std::memory_order_acquire)) {
urgent_pending_.store(true, std::memory_order_release);
return false;
}
return true;
}
void Frame_Pacing_Policy::frame_submitted() {
in_flight_.store(true, std::memory_order_release);
}
void Frame_Pacing_Policy::frame_submitted() {}
bool Frame_Pacing_Policy::frame_completed() {
in_flight_.store(false, std::memory_order_release);
return urgent_pending_.exchange(false, std::memory_order_acq_rel);
return false;
}
void Frame_Pacing_Policy::frame_rejected() {
in_flight_.store(false, std::memory_order_release);
}
void Frame_Pacing_Policy::frame_rejected() {}
}
+3 -5
View File
@@ -15,7 +15,7 @@ struct Frame_Pacing_Properties {
bool render_enabled{true};
bool video_enabled{true};
Frame_Pacing_Mode mode{Frame_Pacing_Mode::fixed_rate};
double fixed_rate_fps{100.0};
double fixed_rate_fps{30.0};
double maximum_rate_fps{100.0};
};
@@ -36,8 +36,8 @@ struct Frame_Pacing_Policy final {
[[nodiscard]] double scheduled_rate_fps() const;
/*
* 周期 tick 是否允许生成新帧。immediate 会提前生成一帧并消费下一次周期机会
* in_flight 只表达 Scene 是否已有一帧尚未完成,不改变 Scene::render(Frame*) 抽象
* 周期 tick 只判断策略/周期语义,不再承担 Scene in-flight 门控
* 实际渲染准入由调用方在 Scene::advance 前控制;忙时保留 latest pending tick
*/
[[nodiscard]] bool accept_periodic_tick(double time_milliseconds);
[[nodiscard]] bool request_immediate();
@@ -49,9 +49,7 @@ private:
template <typename Edit> void update(Edit&& edit);
std::atomic<std::shared_ptr<const Frame_Pacing_Properties>> properties_;
std::atomic_bool in_flight_{};
std::atomic_bool skip_next_periodic_{};
std::atomic_bool urgent_pending_{};
};
}
+22 -7
View File
@@ -137,7 +137,17 @@ struct Frame_Scheduler::Private {
if (!state->alive.load(std::memory_order_acquire)) break;
state->mode = Timer::State::Mode::periodic;
state->period = command.value;
state->next_deadline = now + state->period;
/* 同频 Plot 不要永久锁在同一 deadline 上。稳定 timer id 给
* 每个周期分配固定 phase;时间值仍来自同一全局 origin。 */
{
constexpr std::uint64_t phase_slots = 8;
const auto phase_slot = (state->id - 1U) % phase_slots;
const auto phase = Nanoseconds{
state->period.count() *
static_cast<std::int64_t>(phase_slot) /
static_cast<std::int64_t>(phase_slots)};
state->next_deadline = now + state->period + phase;
}
schedule_state(*state, now);
break;
case Command_Type::once:
@@ -169,13 +179,14 @@ struct Frame_Scheduler::Private {
void fire(Timer::State& state) noexcept {
if (!state.alive.load(std::memory_order_acquire)) return;
const auto now = Scheduler_Clock::now();
const auto issued_at = Scheduler_Clock::now();
const ::Tick sequence = wheel.now();
try {
state.handler(Frame_Scheduler::Tick{
now,
issued_at,
static_cast<std::uint64_t>(sequence),
std::chrono::duration<double, std::milli>(now - origin).count()});
std::chrono::duration<double, std::milli>(
issued_at - origin).count()});
}
catch (...) {
/* Timer callback 是控制面入口,不能因为业务异常终止全局时钟。 */
@@ -189,12 +200,16 @@ struct Frame_Scheduler::Private {
state.period <= Nanoseconds::zero())
return;
/* callback 自身也可能消耗控制线程时间;重取 now,避免用触发前时间
* 重排 deadline 导致已经过期的周期再次被排入 timer wheel。 */
const auto reschedule_now = Scheduler_Clock::now();
state.next_deadline += state.period;
if (state.next_deadline <= now) {
const auto missed = (now - state.next_deadline) / state.period + 1;
if (state.next_deadline <= reschedule_now) {
const auto missed =
(reschedule_now - state.next_deadline) / state.period + 1;
state.next_deadline += state.period * missed;
}
schedule_state(state, now);
schedule_state(state, reschedule_now);
}
void advance_to(Scheduler_Clock::time_point now) {
+63 -25
View File
@@ -156,8 +156,8 @@ private:
std::vector<std::uint64_t> worker_cpu_starts;
std::unique_ptr<Worker_Statistics[]> worker_statistics;
std::size_t worker_statistics_count{};
std::atomic<Render_Frame*> trace_frame{};
std::shared_mutex trace_mutex{}; /* 仅按需捕获时保护 Frame* 获取与关闭。 */
std::unordered_map<std::uint64_t, Render_Frame*> trace_tasks{};
std::shared_mutex trace_mutex{}; /* 按原生节点身份把并发 Taskflow 归属到各自 Frame。 */
std::uint64_t worker_occupation_limit_ns{}; /* 单节点连续非 CPU 等待 Worker 的上限。 */
Task_Overrun_Action worker_overrun_action{}; /* 节点超过占用上限后的处置策略。 */
std::atomic_bool watchdog_stopping{}; /* Watchdog 生命周期停止标志。 */
@@ -433,22 +433,20 @@ public:
record.native_id = static_cast<std::uint64_t>(task.hash_value());
record.type = task.type();
worker_starts.push_back(std::move(record));
auto* frame = trace_frame.load(std::memory_order_acquire);
Render_Frame* frame{};
/*
* 帧租约从 on_entry 持续到对应 on_exit。只在退出时登记写入者会留下
* “捕获已关闭、物理帧已复用、迟到 on_exit 仍访问旧 Frame*”的窗口。
* 每次被追踪的 Taskflow run 在提交前登记 native node -> Frame。
* 因而 Render(N+1) 与外接 H264(N) 可以同时被 Observer 正确归属,
* 不再使用“全局唯一当前 Frame”的假设。帧租约从 on_entry 持续到
* on_exit,避免 graph 退役后迟到的 Observer 写入访问已复用 Frame。
*/
if (frame) {
{
std::shared_lock trace_guard(trace_mutex);
frame = trace_frame.load(std::memory_order_acquire);
if (frame && detail::Taskflow_Frame_Access::acquire_writer(*frame)) {
if (!detail::Taskflow_Frame_Access::contains_task(
*frame, static_cast<std::uint64_t>(task.hash_value()))) {
detail::Taskflow_Frame_Access::release_writer(*frame);
frame = nullptr;
}
}
else frame = nullptr;
const auto found = trace_tasks.find(
static_cast<std::uint64_t>(task.hash_value()));
if (found != trace_tasks.end() &&
detail::Taskflow_Frame_Access::acquire_writer(*found->second))
frame = found->second;
}
worker_starts.back().frame = frame;
const auto active_depth = worker_state.active_depth.fetch_add(
@@ -645,17 +643,44 @@ public:
update_max(worker_state.last_task_time_ns, clock_ns(completed));
}
bool begin_trace(Render_Frame& frame, std::size_t workers) {
std::unique_lock guard(trace_mutex);
const auto current = trace_frame.load(std::memory_order_acquire);
if (current) return current == &frame;
/* Frame 自己保存捕获缓冲;多个 Frame 可以同时处于捕获窗口。 */
detail::Taskflow_Frame_Access::begin_capture(frame, workers);
trace_frame.store(&frame, std::memory_order_release);
return true;
}
void finish_trace(Render_Frame& frame) noexcept {
void activate_graph(Render_Frame& frame, Task_Graph& graph) {
if (!frame.taskflow_trace_requested()) return;
const auto nodes = detail::Task_Graph_Access::nodes(graph);
std::unique_lock guard(trace_mutex);
if (trace_frame.load(std::memory_order_acquire) != &frame) return;
trace_frame.store(nullptr, std::memory_order_release);
for (const auto& node : nodes) {
const auto [found, inserted] = trace_tasks.emplace(
node.native_id, &frame);
if (!inserted && found->second != &frame)
throw std::logic_error(
"concurrent traced runs share the same Taskflow node");
}
}
void deactivate_graph(Render_Frame& frame, Task_Graph& graph) noexcept {
if (!frame.taskflow_trace_requested()) return;
try {
const auto nodes = detail::Task_Graph_Access::nodes(graph);
std::unique_lock guard(trace_mutex);
for (const auto& node : nodes) {
const auto found = trace_tasks.find(node.native_id);
if (found != trace_tasks.end() && found->second == &frame)
trace_tasks.erase(found);
}
}
catch (...) {}
}
void finish_trace(Render_Frame& frame) noexcept {
try {
std::unique_lock guard(trace_mutex);
for (auto it = trace_tasks.begin(); it != trace_tasks.end();) {
if (it->second == &frame) it = trace_tasks.erase(it);
else ++it;
}
}
catch (...) {}
detail::Taskflow_Frame_Access::finish_capture(frame);
}
void write_state(Task_Runtime_State& state, std::size_t workers, std::size_t active_topologies) const {
@@ -929,12 +954,15 @@ public:
std::string_view stage) {
const auto token = detail::Taskflow_Frame_Access::begin_graph(
frame, taskflow, stage);
observer->activate_graph(frame, taskflow);
try {
const auto elapsed = run(taskflow);
observer->deactivate_graph(frame, taskflow);
detail::Taskflow_Frame_Access::finish_graph(token);
return elapsed;
}
catch (...) {
observer->deactivate_graph(frame, taskflow);
detail::Taskflow_Frame_Access::finish_graph(token);
throw;
}
@@ -943,10 +971,20 @@ public:
std::function<void()> completion) {
const auto token = detail::Taskflow_Frame_Access::begin_graph(
frame, taskflow, stage);
run(taskflow, [token, completion = std::move(completion)]() mutable {
try {
observer->activate_graph(frame, taskflow);
run(taskflow, [this, &taskflow, &frame, token,
completion = std::move(completion)]() mutable {
observer->deactivate_graph(frame, taskflow);
detail::Taskflow_Frame_Access::finish_graph(token);
completion();
});
}
catch (...) {
observer->deactivate_graph(frame, taskflow);
detail::Taskflow_Frame_Access::finish_graph(token);
completion();
});
throw;
}
}
void schedule(std::string name, std::function<void()> task) {
if (!task) throw std::invalid_argument("Taskflow scheduled task is empty");