优化
This commit is contained in:
@@ -1,81 +1,39 @@
|
||||
#include "Frame_Pacing_Policy.hpp"
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace aethera {
|
||||
|
||||
Frame_Pacing_Policy::Frame_Pacing_Policy()
|
||||
: properties_(std::make_shared<const Frame_Pacing_Properties>()) {}
|
||||
Frame_Pacing_Policy::Frame_Pacing_Policy() = default;
|
||||
|
||||
Frame_Pacing_Properties Frame_Pacing_Policy::read() const {
|
||||
return *properties_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
template <typename Edit>
|
||||
void Frame_Pacing_Policy::update(Edit&& edit) {
|
||||
auto current = properties_.load(std::memory_order_acquire);
|
||||
for (;;) {
|
||||
auto next = std::make_shared<Frame_Pacing_Properties>(*current);
|
||||
std::forward<Edit>(edit)(*next);
|
||||
std::shared_ptr<const Frame_Pacing_Properties> desired = next;
|
||||
if (properties_.compare_exchange_weak(
|
||||
current, desired, std::memory_order_release,
|
||||
std::memory_order_acquire))
|
||||
return;
|
||||
}
|
||||
return {
|
||||
render_enabled_.load(std::memory_order_acquire),
|
||||
video_enabled_.load(std::memory_order_acquire),
|
||||
mode_.load(std::memory_order_acquire),
|
||||
fixed_rate_fps_.load(std::memory_order_acquire)};
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::set_render_enabled(bool enabled) {
|
||||
update([enabled](auto& value) { value.render_enabled = enabled; });
|
||||
render_enabled_.store(enabled, std::memory_order_release);
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::set_video_enabled(bool enabled) {
|
||||
update([enabled](auto& value) { value.video_enabled = enabled; });
|
||||
video_enabled_.store(enabled, std::memory_order_release);
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::set_mode(Frame_Pacing_Mode mode) {
|
||||
update([mode](auto& value) { value.mode = mode; });
|
||||
mode_.store(mode, std::memory_order_release);
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::set_fixed_rate(double fps) {
|
||||
if (!std::isfinite(fps) || fps <= 0.0)
|
||||
throw std::invalid_argument("frame pacing fps must be positive");
|
||||
update([fps](auto& value) { value.fixed_rate_fps = fps; });
|
||||
fixed_rate_fps_.store(fps, std::memory_order_release);
|
||||
}
|
||||
|
||||
double Frame_Pacing_Policy::scheduled_rate_fps() const {
|
||||
const auto value = read();
|
||||
if (!value.render_enabled || value.mode == Frame_Pacing_Mode::manual)
|
||||
return 0.0;
|
||||
return value.mode == Frame_Pacing_Mode::maximum_rate
|
||||
? value.maximum_rate_fps : value.fixed_rate_fps;
|
||||
bool Frame_Pacing_Policy::request_immediate() const noexcept {
|
||||
return render_enabled_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool Frame_Pacing_Policy::accept_periodic_tick(double time_milliseconds) {
|
||||
static_cast<void>(time_milliseconds);
|
||||
const auto value = read();
|
||||
if (!value.render_enabled || value.mode == Frame_Pacing_Mode::manual)
|
||||
return false;
|
||||
if (skip_next_periodic_.exchange(false, std::memory_order_acq_rel))
|
||||
return false;
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::frame_submitted() {}
|
||||
|
||||
bool Frame_Pacing_Policy::frame_completed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::frame_rejected() {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace aethera {
|
||||
|
||||
@@ -12,11 +11,10 @@ enum struct Frame_Pacing_Mode : std::uint8_t {
|
||||
};
|
||||
|
||||
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{30.0};
|
||||
double maximum_rate_fps{100.0};
|
||||
bool render_enabled{true}; /* 查询时 Plot 是否接受任何帧请求。 */
|
||||
bool video_enabled{true}; /* 查询时完成帧是否需要生成视频像素。 */
|
||||
Frame_Pacing_Mode mode{Frame_Pacing_Mode::fixed_rate}; /* 查询时服务端帧驱动模式。 */
|
||||
double fixed_rate_fps{30.0}; /* fixed_rate 模式目标帧率。 */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -32,24 +30,13 @@ struct Frame_Pacing_Policy final {
|
||||
void set_mode(Frame_Pacing_Mode mode);
|
||||
void set_fixed_rate(double fps);
|
||||
|
||||
/* 当前策略应向全局 Frame_Scheduler 注册的频率;0 表示事件驱动/关闭周期。 */
|
||||
[[nodiscard]] double scheduled_rate_fps() const;
|
||||
|
||||
/*
|
||||
* 周期 tick 只判断策略/周期语义,不再承担 Scene in-flight 门控。
|
||||
* 实际渲染准入由调用方在 Scene::advance 前控制;忙时保留 latest pending tick。
|
||||
*/
|
||||
[[nodiscard]] bool accept_periodic_tick(double time_milliseconds);
|
||||
[[nodiscard]] bool request_immediate();
|
||||
void frame_submitted();
|
||||
[[nodiscard]] bool frame_completed();
|
||||
void frame_rejected();
|
||||
[[nodiscard]] bool request_immediate() const noexcept;
|
||||
|
||||
private:
|
||||
template <typename Edit> void update(Edit&& edit);
|
||||
|
||||
std::atomic<std::shared_ptr<const Frame_Pacing_Properties>> properties_;
|
||||
std::atomic_bool skip_next_periodic_{};
|
||||
std::atomic_bool render_enabled_{true}; /* 当前 Plot 是否接受任何帧请求。 */
|
||||
std::atomic_bool video_enabled_{true}; /* 完成帧是否需要生成视频像素。 */
|
||||
std::atomic<Frame_Pacing_Mode> mode_{Frame_Pacing_Mode::fixed_rate}; /* 当前服务端帧驱动模式。 */
|
||||
std::atomic<double> fixed_rate_fps_{30.0}; /* fixed_rate 模式目标帧率。 */
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
@@ -63,7 +62,6 @@ struct Frame_Scheduler::Private {
|
||||
Command_Type type{};
|
||||
std::shared_ptr<Timer::State> state{};
|
||||
Nanoseconds value{};
|
||||
std::shared_ptr<std::promise<void>> completion{};
|
||||
};
|
||||
|
||||
TimerWheel wheel{};
|
||||
@@ -72,7 +70,6 @@ struct Frame_Scheduler::Private {
|
||||
moodycamel::BlockingConcurrentQueue<Command> commands{256};
|
||||
std::atomic_uint64_t next_id{1};
|
||||
std::atomic_bool stopping{};
|
||||
std::thread::id control_thread_id{};
|
||||
std::jthread thread{};
|
||||
|
||||
Private() : thread([this](std::stop_token stop) { run(stop); }) {}
|
||||
@@ -149,10 +146,6 @@ struct Frame_Scheduler::Private {
|
||||
case Command_Type::stop:
|
||||
break;
|
||||
}
|
||||
if (command.completion) {
|
||||
try { command.completion->set_value(); }
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
void process_commands(Scheduler_Clock::time_point now) {
|
||||
@@ -210,7 +203,6 @@ struct Frame_Scheduler::Private {
|
||||
}
|
||||
|
||||
void run(std::stop_token stop) noexcept {
|
||||
control_thread_id = std::this_thread::get_id();
|
||||
try {
|
||||
while (!stop.stop_requested() &&
|
||||
!stopping.load(std::memory_order_acquire)) {
|
||||
@@ -301,25 +293,6 @@ void Frame_Scheduler::Timer::cancel() noexcept {
|
||||
state_->owner->enqueue({Private::Command_Type::cancel, state_, {}});
|
||||
}
|
||||
|
||||
|
||||
void Frame_Scheduler::Timer::cancel_and_wait() noexcept {
|
||||
if (!state_ || !state_->owner) return;
|
||||
state_->alive.store(false, std::memory_order_release);
|
||||
auto* owner = state_->owner;
|
||||
if (owner->control_thread_id == std::this_thread::get_id()) {
|
||||
state_->event.cancel();
|
||||
state_->mode = State::Mode::stopped;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto completion = std::make_shared<std::promise<void>>();
|
||||
auto future = completion->get_future();
|
||||
owner->enqueue({Private::Command_Type::cancel, state_, {}, completion});
|
||||
future.wait();
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
bool Frame_Scheduler::Timer::valid() const noexcept {
|
||||
return static_cast<bool>(state_);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ struct Frame_Scheduler final {
|
||||
/* 单次延迟;delay <= 0 会在下一个 Scheduler 周期尽快触发。 */
|
||||
void start_once(std::chrono::nanoseconds delay);
|
||||
void cancel() noexcept;
|
||||
void cancel_and_wait() noexcept; /* 销毁拥有 callback 的对象前使用,保证控制线程不再执行该 callback。 */
|
||||
[[nodiscard]] bool valid() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
@@ -24,6 +24,7 @@ struct Render_Frame::Private {
|
||||
std::vector<Taskflow_Task_Trace> tasks{}; /* 仅对应 worker 写入,捕获结束后统一读取。 */
|
||||
};
|
||||
Frame_Identity identity{}; /* 外部帧管理器提供且终生不变的帧身份。 */
|
||||
Frame_Request_Source request_source{}; /* 当前逻辑帧的策略触发来源。 */
|
||||
std::chrono::steady_clock::time_point created_at{}; /* 所有 elapsed_ns 使用的单调时钟原点。 */
|
||||
std::uint64_t created_time_unix_ns{}; /* 用于跨进程展示的创建 Unix 时间,单位为纳秒。 */
|
||||
std::array<std::atomic_uint64_t, marker_count> markers{}; /* 每种时间点首次出现时的 elapsed_ns 加一编码。 */
|
||||
@@ -35,8 +36,10 @@ struct Render_Frame::Private {
|
||||
Render_Frame::Render_Frame(Frame_Identity identity) : d(std::make_unique<Private>()) {
|
||||
begin(identity);
|
||||
}
|
||||
void Render_Frame::begin(Frame_Identity identity) noexcept {
|
||||
void Render_Frame::begin(Frame_Identity identity,
|
||||
Frame_Request_Source source) noexcept {
|
||||
d->identity = identity;
|
||||
d->request_source = source;
|
||||
d->created_at = std::chrono::steady_clock::now();
|
||||
const auto system_now = std::chrono::system_clock::now().time_since_epoch();
|
||||
d->created_time_unix_ns = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(system_now).count());
|
||||
@@ -154,6 +157,7 @@ bool Render_Frame::taskflow_trace_requested() const noexcept {
|
||||
Taskflow_Frame_Trace Render_Frame::take_taskflow_trace() {
|
||||
Taskflow_Frame_Trace result{};
|
||||
result.identity = d->identity;
|
||||
result.request_source = d->request_source;
|
||||
result.created_time_unix_ns = d->created_time_unix_ns;
|
||||
result.worker_count = d->taskflow_workers.size();
|
||||
for (std::size_t index = 0; index < marker_count; ++index) {
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace detail {
|
||||
struct Taskflow_Frame_Access;
|
||||
}
|
||||
struct Frame_Statistics_Sample;
|
||||
enum struct Frame_Request_Source : std::uint8_t {
|
||||
unspecified,
|
||||
periodic,
|
||||
immediate,
|
||||
maximum_rate
|
||||
};
|
||||
enum struct Frame_Trace_Marker : std::uint8_t {
|
||||
created,
|
||||
plot_update_started,
|
||||
@@ -121,6 +127,7 @@ struct Taskflow_Task_Trace {
|
||||
};
|
||||
struct Taskflow_Frame_Trace {
|
||||
Frame_Identity identity{}; /* 该执行图所属逻辑渲染帧。 */
|
||||
Frame_Request_Source request_source{}; /* 该逻辑帧由周期、手动或最大吞吐策略触发。 */
|
||||
std::uint64_t created_time_unix_ns{}; /* 帧创建 Unix 时间,单位纳秒。 */
|
||||
std::size_t worker_count{}; /* 捕获时全局 Executor 的 worker 数。 */
|
||||
std::vector<Frame_Trace_Point> markers{}; /* 与该帧 DAG 共用时间原点的原始流水线时间点。 */
|
||||
@@ -142,7 +149,8 @@ public:
|
||||
[[nodiscard]] Taskflow_Frame_Trace take_taskflow_trace();
|
||||
protected:
|
||||
/* 物理帧槽再次承载新逻辑帧时,重建其唯一身份和诊断时间原点。 */
|
||||
void begin(Frame_Identity identity) noexcept;
|
||||
void begin(Frame_Identity identity,
|
||||
Frame_Request_Source source = Frame_Request_Source::unspecified) noexcept;
|
||||
private:
|
||||
struct Private;
|
||||
friend struct detail::Taskflow_Frame_Access;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "scene.hpp"
|
||||
#include "Frame_Pacing_Policy.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
@@ -50,6 +51,24 @@ using Scene = aethera::Scene;
|
||||
inline Direct_Renderable::Private& Direct_Renderable::data_for_test() {
|
||||
return static_cast<Private&>(*d);
|
||||
}
|
||||
|
||||
TEST(frame_pacing_policy, independent_properties_do_not_publish_copied_versions) {
|
||||
aethera::Frame_Pacing_Policy policy;
|
||||
policy.set_mode(aethera::Frame_Pacing_Mode::maximum_rate);
|
||||
policy.set_fixed_rate(47.5);
|
||||
policy.set_video_enabled(false);
|
||||
|
||||
const auto properties = policy.read();
|
||||
EXPECT_TRUE(properties.render_enabled);
|
||||
EXPECT_FALSE(properties.video_enabled);
|
||||
EXPECT_EQ(properties.mode, aethera::Frame_Pacing_Mode::maximum_rate);
|
||||
EXPECT_DOUBLE_EQ(properties.fixed_rate_fps, 47.5);
|
||||
EXPECT_TRUE(policy.request_immediate());
|
||||
|
||||
policy.set_render_enabled(false);
|
||||
EXPECT_FALSE(policy.request_immediate());
|
||||
EXPECT_THROW(policy.set_fixed_rate(0.0), std::invalid_argument);
|
||||
}
|
||||
inline Graph_Renderable::Private& Graph_Renderable::data_for_test() {
|
||||
return static_cast<Private&>(*d);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user