帧策略展示优化
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
#include "Frame_Pacing_Policy.hpp"
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace aethera {
|
||||
|
||||
Frame_Pacing_Policy::Frame_Pacing_Policy() = default;
|
||||
|
||||
Frame_Pacing_Properties Frame_Pacing_Policy::read() const {
|
||||
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) {
|
||||
render_enabled_.store(enabled, std::memory_order_release);
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::set_video_enabled(bool enabled) {
|
||||
video_enabled_.store(enabled, std::memory_order_release);
|
||||
}
|
||||
|
||||
void Frame_Pacing_Policy::set_mode(Frame_Pacing_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");
|
||||
fixed_rate_fps_.store(fps, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool Frame_Pacing_Policy::request_immediate() const noexcept {
|
||||
return render_enabled_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace aethera {
|
||||
|
||||
enum struct Frame_Pacing_Mode : std::uint8_t {
|
||||
manual,
|
||||
fixed_rate,
|
||||
maximum_rate
|
||||
};
|
||||
|
||||
struct Frame_Pacing_Properties {
|
||||
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 模式目标帧率。 */
|
||||
};
|
||||
|
||||
/*
|
||||
* 每个 Scene/Plot 独立持有的帧策略。
|
||||
* Scheduler 只负责 WHEN;这里负责 WHETHER。
|
||||
*/
|
||||
struct Frame_Pacing_Policy final {
|
||||
Frame_Pacing_Policy();
|
||||
|
||||
[[nodiscard]] Frame_Pacing_Properties read() const;
|
||||
void set_render_enabled(bool enabled);
|
||||
void set_video_enabled(bool enabled);
|
||||
void set_mode(Frame_Pacing_Mode mode);
|
||||
void set_fixed_rate(double fps);
|
||||
|
||||
[[nodiscard]] bool request_immediate() const noexcept;
|
||||
|
||||
private:
|
||||
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 模式目标帧率。 */
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
#include "Frame_Policy.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace aethera {
|
||||
namespace {
|
||||
|
||||
std::uint64_t monotonic_nanoseconds(
|
||||
std::chrono::steady_clock::time_point value =
|
||||
std::chrono::steady_clock::now()) noexcept {
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
value.time_since_epoch()).count());
|
||||
}
|
||||
|
||||
std::uint64_t duration_nanoseconds(
|
||||
std::chrono::steady_clock::duration value) noexcept {
|
||||
return static_cast<std::uint64_t>(std::max<std::int64_t>(
|
||||
0, std::chrono::duration_cast<std::chrono::nanoseconds>(value).count()));
|
||||
}
|
||||
|
||||
void observe_event(Frame_Policy::State& state,
|
||||
const detail::Frame_Policy_Event& event) noexcept {
|
||||
if (event.occurred_ns != 0) {
|
||||
if (state.observation_started_ns == 0)
|
||||
state.observation_started_ns = event.occurred_ns;
|
||||
state.observed_until_ns = std::max(
|
||||
state.observed_until_ns, event.occurred_ns);
|
||||
}
|
||||
}
|
||||
|
||||
void reset_counters(Frame_Policy::State& state,
|
||||
std::uint64_t occurred_ns) noexcept {
|
||||
const auto generation = state.generation + 1;
|
||||
const auto render_enabled = state.render_enabled;
|
||||
const auto video_enabled = state.video_enabled;
|
||||
const auto mode = state.mode;
|
||||
const auto fixed_rate_fps = state.fixed_rate_fps;
|
||||
state = {};
|
||||
state.generation = generation;
|
||||
state.render_enabled = render_enabled;
|
||||
state.video_enabled = video_enabled;
|
||||
state.mode = mode;
|
||||
state.fixed_rate_fps = fixed_rate_fps;
|
||||
state.observation_started_ns = occurred_ns;
|
||||
state.observed_until_ns = occurred_ns;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Frame_Policy::submit(detail::Frame_Policy_Event event) {
|
||||
if (event.occurred_ns == 0) event.occurred_ns = monotonic_nanoseconds();
|
||||
submit_stream<detail::Frame_Policy_Event_Stream>(std::move(event));
|
||||
}
|
||||
|
||||
void Frame_Policy::set_render_enabled(bool enabled) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::render_enabled,
|
||||
.enabled = enabled});
|
||||
}
|
||||
|
||||
void Frame_Policy::set_video_enabled(bool enabled) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::video_enabled,
|
||||
.enabled = enabled});
|
||||
}
|
||||
|
||||
void Frame_Policy::set_mode(Frame_Pacing_Mode mode) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::mode, .mode = mode});
|
||||
}
|
||||
|
||||
void Frame_Policy::set_fixed_rate(double fps) {
|
||||
if (!std::isfinite(fps) || fps <= 0.0)
|
||||
throw std::invalid_argument("frame pacing fps must be positive");
|
||||
submit({.type = detail::Frame_Policy_Event_Type::fixed_rate,
|
||||
.number = fps});
|
||||
}
|
||||
|
||||
void Frame_Policy::reset_statistics() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::reset});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_request(
|
||||
Frame_Request_Source source,
|
||||
std::chrono::steady_clock::time_point occurred_at) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::request_received,
|
||||
.source = source,
|
||||
.occurred_ns = monotonic_nanoseconds(occurred_at)});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_request_accepted(Frame_Request_Source source) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::request_accepted,
|
||||
.source = source});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_request_coalesced() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::request_coalesced});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_policy_rejection() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::policy_rejected});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_frame_slot_backpressure() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::frame_slot_backpressure});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_scene_rejection() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::scene_rejected});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_frame_submitted(
|
||||
std::uint64_t sequence, Frame_Request_Source source,
|
||||
std::chrono::steady_clock::duration tick_queue_duration) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::frame_submitted,
|
||||
.source = source,
|
||||
.sequence = sequence,
|
||||
.duration_ns = duration_nanoseconds(tick_queue_duration)});
|
||||
}
|
||||
|
||||
void Frame_Policy::record_frame_completed(
|
||||
std::uint64_t sequence,
|
||||
std::chrono::steady_clock::duration completion_latency) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::frame_completed,
|
||||
.sequence = sequence,
|
||||
.duration_ns = duration_nanoseconds(completion_latency)});
|
||||
}
|
||||
|
||||
bool Frame_Policy::consume_events() {
|
||||
exchange_stream<detail::Frame_Policy_Event_Stream>();
|
||||
bool consumed{};
|
||||
bool configuration_changed{};
|
||||
auto& state = double_buffer::detail::Internal_Access::pending_state(this);
|
||||
access_rendering_stream<detail::Frame_Policy_Event_Stream>(
|
||||
[&](std::span<detail::Frame_Policy_Event> events) {
|
||||
for (const auto& event : events) {
|
||||
consumed = true;
|
||||
observe_event(state, event);
|
||||
switch (event.type) {
|
||||
case detail::Frame_Policy_Event_Type::render_enabled:
|
||||
configuration_changed |= state.render_enabled != event.enabled;
|
||||
state.render_enabled = event.enabled;
|
||||
++state.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::video_enabled:
|
||||
state.video_enabled = event.enabled;
|
||||
++state.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::mode:
|
||||
configuration_changed |= state.mode != event.mode;
|
||||
state.mode = event.mode;
|
||||
++state.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::fixed_rate:
|
||||
configuration_changed |= state.fixed_rate_fps != event.number;
|
||||
state.fixed_rate_fps = event.number;
|
||||
++state.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::request_received:
|
||||
++state.request_count;
|
||||
if (event.source == Frame_Request_Source::periodic)
|
||||
++state.periodic_request_count;
|
||||
else if (event.source == Frame_Request_Source::immediate)
|
||||
++state.immediate_request_count;
|
||||
else if (event.source == Frame_Request_Source::maximum_rate)
|
||||
++state.maximum_rate_request_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::request_accepted:
|
||||
++state.accepted_request_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::request_coalesced:
|
||||
++state.coalesced_request_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::policy_rejected:
|
||||
++state.policy_rejection_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::frame_slot_backpressure:
|
||||
++state.frame_slot_backpressure_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::scene_rejected:
|
||||
++state.scene_rejection_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::frame_submitted:
|
||||
++state.submitted_frame_count;
|
||||
++state.active_frame_count;
|
||||
state.last_frame_sequence = event.sequence;
|
||||
state.last_request_source = event.source;
|
||||
state.latest_tick_queue_ns = event.duration_ns;
|
||||
state.tick_queue_total_ns += event.duration_ns;
|
||||
state.maximum_tick_queue_ns = std::max(
|
||||
state.maximum_tick_queue_ns, event.duration_ns);
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::frame_completed: {
|
||||
++state.completed_frame_count;
|
||||
if (state.active_frame_count != 0) --state.active_frame_count;
|
||||
state.last_frame_sequence = event.sequence;
|
||||
state.latest_completion_latency_ns = event.duration_ns;
|
||||
state.completion_latency_total_ns += event.duration_ns;
|
||||
state.maximum_completion_latency_ns = std::max(
|
||||
state.maximum_completion_latency_ns, event.duration_ns);
|
||||
if (state.first_completion_ns == 0)
|
||||
state.first_completion_ns = event.occurred_ns;
|
||||
if (state.last_completion_ns != 0) {
|
||||
const auto interval = event.occurred_ns -
|
||||
state.last_completion_ns;
|
||||
++state.completion_interval_count;
|
||||
state.latest_completion_interval_ns = interval;
|
||||
state.completion_interval_total_ns += interval;
|
||||
const auto value = static_cast<double>(interval);
|
||||
state.completion_interval_squared_total_ns2 +=
|
||||
value * value;
|
||||
}
|
||||
state.last_completion_ns = event.occurred_ns;
|
||||
break;
|
||||
}
|
||||
case detail::Frame_Policy_Event_Type::reset:
|
||||
reset_counters(state, event.occurred_ns);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (consumed) publish_state<Base_Tag>();
|
||||
return configuration_changed;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
#include "double_buffer/model.hpp"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace aethera {
|
||||
|
||||
enum struct Frame_Pacing_Mode : std::uint8_t {
|
||||
manual,
|
||||
fixed_rate,
|
||||
maximum_rate
|
||||
};
|
||||
|
||||
enum struct Frame_Request_Source : std::uint8_t {
|
||||
unspecified,
|
||||
periodic,
|
||||
immediate,
|
||||
maximum_rate
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct Frame_Policy_Event_Stream;
|
||||
enum struct Frame_Policy_Event_Type : std::uint8_t {
|
||||
render_enabled,
|
||||
video_enabled,
|
||||
mode,
|
||||
fixed_rate,
|
||||
request_received,
|
||||
request_accepted,
|
||||
request_coalesced,
|
||||
policy_rejected,
|
||||
frame_slot_backpressure,
|
||||
scene_rejected,
|
||||
frame_submitted,
|
||||
frame_completed,
|
||||
reset
|
||||
};
|
||||
struct Frame_Policy_Event {
|
||||
Frame_Policy_Event_Type type{};
|
||||
Frame_Request_Source source{};
|
||||
Frame_Pacing_Mode mode{};
|
||||
std::uint64_t sequence{};
|
||||
std::uint64_t occurred_ns{};
|
||||
std::uint64_t duration_ns{};
|
||||
double number{};
|
||||
bool enabled{};
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* 帧策略的配置、累计事实和发布状态均归属同一个 double-buffer 对象。
|
||||
* 任意线程只向 MPMC 事件流提交事实;Plot tick consumer 是唯一归并写者。
|
||||
* 查询方只读取 current State,不参与事件归并,也不获取 Plot/Scene 锁。
|
||||
*/
|
||||
struct Frame_Policy final : double_buffer::Def<
|
||||
Frame_Policy, double_buffer::Root,
|
||||
double_buffer::Mpmc_Triple_Buffer<
|
||||
detail::Frame_Policy_Event_Stream, detail::Frame_Policy_Event>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
std::uint64_t generation{};
|
||||
bool render_enabled{true};
|
||||
bool video_enabled{true};
|
||||
Frame_Pacing_Mode mode{Frame_Pacing_Mode::fixed_rate};
|
||||
double fixed_rate_fps{30.0};
|
||||
|
||||
std::uint64_t observation_started_ns{};
|
||||
std::uint64_t observed_until_ns{};
|
||||
std::uint64_t request_count{};
|
||||
std::uint64_t periodic_request_count{};
|
||||
std::uint64_t immediate_request_count{};
|
||||
std::uint64_t maximum_rate_request_count{};
|
||||
std::uint64_t accepted_request_count{};
|
||||
std::uint64_t coalesced_request_count{};
|
||||
std::uint64_t policy_rejection_count{};
|
||||
std::uint64_t frame_slot_backpressure_count{};
|
||||
std::uint64_t scene_rejection_count{};
|
||||
std::uint64_t submitted_frame_count{};
|
||||
std::uint64_t completed_frame_count{};
|
||||
std::uint64_t active_frame_count{};
|
||||
|
||||
std::uint64_t first_completion_ns{};
|
||||
std::uint64_t last_completion_ns{};
|
||||
std::uint64_t completion_interval_count{};
|
||||
std::uint64_t latest_completion_interval_ns{};
|
||||
std::uint64_t completion_interval_total_ns{};
|
||||
double completion_interval_squared_total_ns2{};
|
||||
std::uint64_t latest_tick_queue_ns{};
|
||||
std::uint64_t tick_queue_total_ns{};
|
||||
std::uint64_t maximum_tick_queue_ns{};
|
||||
std::uint64_t latest_completion_latency_ns{};
|
||||
std::uint64_t completion_latency_total_ns{};
|
||||
std::uint64_t maximum_completion_latency_ns{};
|
||||
std::uint64_t last_frame_sequence{};
|
||||
Frame_Request_Source last_request_source{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {};
|
||||
|
||||
void set_render_enabled(bool enabled);
|
||||
void set_video_enabled(bool enabled);
|
||||
void set_mode(Frame_Pacing_Mode mode);
|
||||
void set_fixed_rate(double fps);
|
||||
void reset_statistics();
|
||||
|
||||
void record_request(Frame_Request_Source source,
|
||||
std::chrono::steady_clock::time_point occurred_at);
|
||||
void record_request_accepted(Frame_Request_Source source);
|
||||
void record_request_coalesced();
|
||||
void record_policy_rejection();
|
||||
void record_frame_slot_backpressure();
|
||||
void record_scene_rejection();
|
||||
void record_frame_submitted(
|
||||
std::uint64_t sequence, Frame_Request_Source source,
|
||||
std::chrono::steady_clock::duration tick_queue_duration);
|
||||
void record_frame_completed(
|
||||
std::uint64_t sequence,
|
||||
std::chrono::steady_clock::duration completion_latency);
|
||||
|
||||
/* 唯一消费者在短任务中归并全部待处理事件并发布 State。 */
|
||||
[[nodiscard]] bool consume_events();
|
||||
|
||||
private:
|
||||
void submit(detail::Frame_Policy_Event event);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@ struct Render_Frame::Private {
|
||||
};
|
||||
Frame_Identity identity{}; /* 外部帧管理器提供且终生不变的帧身份。 */
|
||||
Frame_Request_Source request_source{}; /* 当前逻辑帧的策略触发来源。 */
|
||||
Frame_Policy::State frame_policy{}; /* 创建逻辑帧时读取的已发布策略状态。 */
|
||||
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 加一编码。 */
|
||||
@@ -37,9 +38,11 @@ Render_Frame::Render_Frame(Frame_Identity identity) : d(std::make_unique<Private
|
||||
begin(identity);
|
||||
}
|
||||
void Render_Frame::begin(Frame_Identity identity,
|
||||
Frame_Request_Source source) noexcept {
|
||||
Frame_Request_Source source,
|
||||
const Frame_Policy::State& frame_policy) noexcept {
|
||||
d->identity = identity;
|
||||
d->request_source = source;
|
||||
d->frame_policy = frame_policy;
|
||||
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());
|
||||
@@ -158,6 +161,7 @@ Taskflow_Frame_Trace Render_Frame::take_taskflow_trace() {
|
||||
Taskflow_Frame_Trace result{};
|
||||
result.identity = d->identity;
|
||||
result.request_source = d->request_source;
|
||||
result.frame_policy = d->frame_policy;
|
||||
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) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "Frame_Policy/Frame_Policy.hpp"
|
||||
#include "double_buffer/mechanism.hpp"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -9,12 +10,6 @@ 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,
|
||||
@@ -128,6 +123,7 @@ struct Taskflow_Task_Trace {
|
||||
struct Taskflow_Frame_Trace {
|
||||
Frame_Identity identity{}; /* 该执行图所属逻辑渲染帧。 */
|
||||
Frame_Request_Source request_source{}; /* 该逻辑帧由周期、手动或最大吞吐策略触发。 */
|
||||
Frame_Policy::State frame_policy{}; /* 帧创建边界读取的已发布策略状态。 */
|
||||
std::uint64_t created_time_unix_ns{}; /* 帧创建 Unix 时间,单位纳秒。 */
|
||||
std::size_t worker_count{}; /* 捕获时全局 Executor 的 worker 数。 */
|
||||
std::vector<Frame_Trace_Point> markers{}; /* 与该帧 DAG 共用时间原点的原始流水线时间点。 */
|
||||
@@ -150,7 +146,8 @@ public:
|
||||
protected:
|
||||
/* 物理帧槽再次承载新逻辑帧时,重建其唯一身份和诊断时间原点。 */
|
||||
void begin(Frame_Identity identity,
|
||||
Frame_Request_Source source = Frame_Request_Source::unspecified) noexcept;
|
||||
Frame_Request_Source source = Frame_Request_Source::unspecified,
|
||||
const Frame_Policy::State& frame_policy = {}) noexcept;
|
||||
private:
|
||||
struct Private;
|
||||
friend struct detail::Taskflow_Frame_Access;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "scene.hpp"
|
||||
#include "Frame_Pacing_Policy.hpp"
|
||||
#include "Frame_Policy/Frame_Policy.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
@@ -52,22 +52,31 @@ 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);
|
||||
TEST(frame_policy, mpmc_events_publish_one_double_buffer_state) {
|
||||
auto built = aethera::Frame_Policy::Builder<aethera::Frame_Policy>{}.build();
|
||||
ASSERT_TRUE(built.has_value());
|
||||
auto policy = std::move(*built);
|
||||
policy->set_mode(aethera::Frame_Pacing_Mode::maximum_rate);
|
||||
policy->set_fixed_rate(47.5);
|
||||
policy->set_video_enabled(false);
|
||||
policy->record_request(aethera::Frame_Request_Source::maximum_rate,
|
||||
std::chrono::steady_clock::now());
|
||||
policy->record_request_accepted(aethera::Frame_Request_Source::maximum_rate);
|
||||
policy->record_frame_submitted(
|
||||
7, aethera::Frame_Request_Source::maximum_rate,
|
||||
std::chrono::microseconds{250});
|
||||
EXPECT_TRUE(policy->consume_events());
|
||||
|
||||
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);
|
||||
const auto& state = policy->read_state<aethera::Frame_Policy::Base_Tag>();
|
||||
EXPECT_TRUE(state.render_enabled);
|
||||
EXPECT_FALSE(state.video_enabled);
|
||||
EXPECT_EQ(state.mode, aethera::Frame_Pacing_Mode::maximum_rate);
|
||||
EXPECT_DOUBLE_EQ(state.fixed_rate_fps, 47.5);
|
||||
EXPECT_EQ(state.request_count, 1);
|
||||
EXPECT_EQ(state.accepted_request_count, 1);
|
||||
EXPECT_EQ(state.submitted_frame_count, 1);
|
||||
EXPECT_EQ(state.latest_tick_queue_ns, 250'000);
|
||||
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