修复崩溃
This commit is contained in:
@@ -52,7 +52,7 @@ struct Frame_Policy_Event {
|
||||
* 任意线程只向 MPMC 事件流提交事实;Plot tick consumer 是唯一归并写者。
|
||||
* 查询方只读取 current State,不参与事件归并,也不获取 Plot/Scene 锁。
|
||||
*/
|
||||
struct Frame_Policy : double_buffer::Def<
|
||||
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>> {
|
||||
|
||||
@@ -1,9 +1,235 @@
|
||||
#include "Frame_Policy_3D.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace aethera {
|
||||
namespace {
|
||||
|
||||
std::uint64_t monotonic_nanoseconds_3d(
|
||||
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_3d(
|
||||
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_common_event(Frame_Policy::State& state,
|
||||
const detail::Frame_Policy_Event& event) noexcept {
|
||||
if (event.occurred_ns == 0) return;
|
||||
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_common_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_3D::submit(detail::Frame_Policy_Event event) {
|
||||
if (event.occurred_ns == 0)
|
||||
event.occurred_ns = monotonic_nanoseconds_3d();
|
||||
submit_stream<detail::Frame_Policy_Event_Stream>(std::move(event));
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::set_render_enabled(bool enabled) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::render_enabled,
|
||||
.enabled = enabled});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::set_video_enabled(bool enabled) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::video_enabled,
|
||||
.enabled = enabled});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::set_mode(Frame_Pacing_Mode mode) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::mode, .mode = mode});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::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_3D::reset_statistics() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::reset});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::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_3d(occurred_at)});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::record_request_accepted(Frame_Request_Source source) {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::request_accepted,
|
||||
.source = source});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::record_request_coalesced() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::request_coalesced});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::record_policy_rejection() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::policy_rejected});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::record_frame_slot_backpressure() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::frame_slot_backpressure});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::record_scene_rejection() {
|
||||
submit({.type = detail::Frame_Policy_Event_Type::scene_rejected});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::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_3d(tick_queue_duration)});
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::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_3d(completion_latency)});
|
||||
}
|
||||
|
||||
bool Frame_Policy_3D::consume_events() {
|
||||
exchange_stream<detail::Frame_Policy_Event_Stream>();
|
||||
bool consumed{};
|
||||
bool configuration_changed{};
|
||||
auto& state = static_cast<State&>(
|
||||
double_buffer::detail::Internal_Access::pending_state(this));
|
||||
auto& common = state.common;
|
||||
access_rendering_stream<detail::Frame_Policy_Event_Stream>(
|
||||
[&](std::span<detail::Frame_Policy_Event> events) {
|
||||
for (const auto& event : events) {
|
||||
consumed = true;
|
||||
observe_common_event(common, event);
|
||||
switch (event.type) {
|
||||
case detail::Frame_Policy_Event_Type::render_enabled:
|
||||
configuration_changed |=
|
||||
common.render_enabled != event.enabled;
|
||||
common.render_enabled = event.enabled;
|
||||
++common.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::video_enabled:
|
||||
common.video_enabled = event.enabled;
|
||||
++common.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::mode:
|
||||
configuration_changed |= common.mode != event.mode;
|
||||
common.mode = event.mode;
|
||||
++common.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::fixed_rate:
|
||||
configuration_changed |=
|
||||
common.fixed_rate_fps != event.number;
|
||||
common.fixed_rate_fps = event.number;
|
||||
++common.generation;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::request_received:
|
||||
++common.request_count;
|
||||
if (event.source == Frame_Request_Source::periodic)
|
||||
++common.periodic_request_count;
|
||||
else if (event.source == Frame_Request_Source::immediate)
|
||||
++common.immediate_request_count;
|
||||
else if (event.source == Frame_Request_Source::maximum_rate)
|
||||
++common.maximum_rate_request_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::request_accepted:
|
||||
++common.accepted_request_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::request_coalesced:
|
||||
++common.coalesced_request_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::policy_rejected:
|
||||
++common.policy_rejection_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::frame_slot_backpressure:
|
||||
++common.frame_slot_backpressure_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::scene_rejected:
|
||||
++common.scene_rejection_count;
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::frame_submitted:
|
||||
++common.submitted_frame_count;
|
||||
++common.active_frame_count;
|
||||
common.last_frame_sequence = event.sequence;
|
||||
common.last_request_source = event.source;
|
||||
common.latest_tick_queue_ns = event.duration_ns;
|
||||
common.tick_queue_total_ns += event.duration_ns;
|
||||
common.maximum_tick_queue_ns = std::max(
|
||||
common.maximum_tick_queue_ns, event.duration_ns);
|
||||
break;
|
||||
case detail::Frame_Policy_Event_Type::frame_completed: {
|
||||
++common.completed_frame_count;
|
||||
if (common.active_frame_count != 0)
|
||||
--common.active_frame_count;
|
||||
common.last_frame_sequence = event.sequence;
|
||||
common.latest_completion_latency_ns = event.duration_ns;
|
||||
common.completion_latency_total_ns += event.duration_ns;
|
||||
common.maximum_completion_latency_ns = std::max(
|
||||
common.maximum_completion_latency_ns,
|
||||
event.duration_ns);
|
||||
if (common.first_completion_ns == 0)
|
||||
common.first_completion_ns = event.occurred_ns;
|
||||
if (common.last_completion_ns != 0) {
|
||||
const auto interval = event.occurred_ns -
|
||||
common.last_completion_ns;
|
||||
++common.completion_interval_count;
|
||||
common.latest_completion_interval_ns = interval;
|
||||
common.completion_interval_total_ns += interval;
|
||||
const auto value = static_cast<double>(interval);
|
||||
common.completion_interval_squared_total_ns2 +=
|
||||
value * value;
|
||||
}
|
||||
common.last_completion_ns = event.occurred_ns;
|
||||
break;
|
||||
}
|
||||
case detail::Frame_Policy_Event_Type::reset:
|
||||
reset_common_counters(common, event.occurred_ns);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (consumed) publish_state<Base_Tag>();
|
||||
return configuration_changed;
|
||||
}
|
||||
|
||||
void Frame_Policy_3D::submit_3d(detail::Frame_Policy_3D_Event event) {
|
||||
submit_stream<detail::Frame_Policy_3D_Event_Stream>(std::move(event));
|
||||
}
|
||||
@@ -50,10 +276,14 @@ bool Frame_Policy_3D::consume_3d_events() {
|
||||
break;
|
||||
case detail::Frame_Policy_3D_Event_Type::reset: {
|
||||
const auto in_flight = state.gpu_in_flight;
|
||||
state = {};
|
||||
state.pipeline_capacity = 3;
|
||||
state.gpu_submission_count = 0;
|
||||
state.overlapped_release_count = 0;
|
||||
state.completion_gated_release_count = 0;
|
||||
state.gpu_completion_count = 0;
|
||||
state.gpu_in_flight = in_flight;
|
||||
state.peak_gpu_in_flight = in_flight;
|
||||
state.last_submitted_sequence = 0;
|
||||
state.last_completed_sequence = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,16 @@ struct Frame_Policy_3D_Event {
|
||||
* 事件流进入,Plot 的唯一 tick consumer 归并并通过 double-buffer 发布。
|
||||
*/
|
||||
struct Frame_Policy_3D final : double_buffer::Def<
|
||||
Frame_Policy_3D, Frame_Policy,
|
||||
Frame_Policy_3D, double_buffer::Root,
|
||||
double_buffer::Mpmc_Triple_Buffer<
|
||||
detail::Frame_Policy_Event_Stream,
|
||||
detail::Frame_Policy_Event>,
|
||||
double_buffer::Mpmc_Triple_Buffer<
|
||||
detail::Frame_Policy_3D_Event_Stream,
|
||||
detail::Frame_Policy_3D_Event>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
Frame_Policy::State common{}; /* 3D 策略拥有的唯一公共节奏状态,不别名 2D 存储。 */
|
||||
std::size_t pipeline_capacity{3}; /* Scene 可绑定的物理 3D 帧槽数量。 */
|
||||
std::uint64_t gpu_submission_count{}; /* Vulkan 实际提交的累计帧数。 */
|
||||
std::uint64_t overlapped_release_count{}; /* 提交后即开放下一次 Prepare 的累计帧数。 */
|
||||
@@ -42,12 +46,34 @@ struct Frame_Policy_3D final : double_buffer::Def<
|
||||
};
|
||||
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);
|
||||
[[nodiscard]] bool consume_events();
|
||||
|
||||
void record_gpu_submitted(std::uint64_t sequence, bool overlaps_gpu);
|
||||
void record_gpu_completed(std::uint64_t sequence);
|
||||
void reset_3d_statistics();
|
||||
[[nodiscard]] bool consume_3d_events();
|
||||
|
||||
private:
|
||||
void submit(detail::Frame_Policy_Event event);
|
||||
void submit_3d(detail::Frame_Policy_3D_Event event);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Frame_Policy/Frame_Policy_3D.hpp"
|
||||
#include <chrono>
|
||||
#include <gtest/gtest.h>
|
||||
#include <utility>
|
||||
|
||||
@@ -27,12 +28,52 @@ TEST(frame_policy_3d, publishes_gpu_pipeline_facts_without_changing_common_polic
|
||||
EXPECT_EQ(pipeline.last_submitted_sequence, 12u);
|
||||
EXPECT_EQ(pipeline.last_completed_sequence, 11u);
|
||||
|
||||
const auto& common = policy->read_state<aethera::Frame_Policy::Base_Tag>();
|
||||
const auto& common = pipeline.common;
|
||||
EXPECT_EQ(common.submitted_frame_count, 0u);
|
||||
EXPECT_EQ(common.completed_frame_count, 0u);
|
||||
EXPECT_EQ(common.mode, aethera::Frame_Pacing_Mode::fixed_rate);
|
||||
}
|
||||
|
||||
TEST(frame_policy_3d, owns_common_event_stream_without_base_storage_aliasing) {
|
||||
auto built = aethera::Frame_Policy_3D::Builder<
|
||||
aethera::Frame_Policy_3D>{}.build();
|
||||
ASSERT_TRUE(built.has_value());
|
||||
auto policy = std::move(*built);
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
policy->set_mode(aethera::Frame_Pacing_Mode::maximum_rate);
|
||||
policy->record_request(aethera::Frame_Request_Source::maximum_rate, now);
|
||||
policy->record_request_accepted(
|
||||
aethera::Frame_Request_Source::maximum_rate);
|
||||
policy->record_frame_submitted(
|
||||
31, aethera::Frame_Request_Source::maximum_rate,
|
||||
std::chrono::microseconds{40});
|
||||
policy->record_frame_completed(31, std::chrono::microseconds{90});
|
||||
policy->record_gpu_submitted(31, true);
|
||||
EXPECT_TRUE(policy->consume_events());
|
||||
EXPECT_TRUE(policy->consume_3d_events());
|
||||
|
||||
const auto& state = policy->read_state<
|
||||
aethera::Frame_Policy_3D::Base_Tag>();
|
||||
EXPECT_EQ(state.common.mode, aethera::Frame_Pacing_Mode::maximum_rate);
|
||||
EXPECT_EQ(state.common.request_count, 1u);
|
||||
EXPECT_EQ(state.common.accepted_request_count, 1u);
|
||||
EXPECT_EQ(state.common.submitted_frame_count, 1u);
|
||||
EXPECT_EQ(state.common.completed_frame_count, 1u);
|
||||
EXPECT_EQ(state.common.active_frame_count, 0u);
|
||||
EXPECT_EQ(state.common.last_frame_sequence, 31u);
|
||||
EXPECT_EQ(state.gpu_submission_count, 1u);
|
||||
EXPECT_EQ(state.gpu_in_flight, 1u);
|
||||
|
||||
policy->reset_statistics();
|
||||
static_cast<void>(policy->consume_events());
|
||||
const auto& reset = policy->read_state<
|
||||
aethera::Frame_Policy_3D::Base_Tag>();
|
||||
EXPECT_EQ(reset.common.request_count, 0u);
|
||||
EXPECT_EQ(reset.gpu_submission_count, 1u);
|
||||
EXPECT_EQ(reset.gpu_in_flight, 1u);
|
||||
}
|
||||
|
||||
TEST(frame_policy_3d, reset_preserves_only_current_in_flight_ownership) {
|
||||
auto built = aethera::Frame_Policy_3D::Builder<
|
||||
aethera::Frame_Policy_3D>{}.build();
|
||||
|
||||
+98
-44
@@ -77,8 +77,19 @@ std::string_view pixel_format_name(render_3d::Pixel_Format format) {
|
||||
throw std::logic_error("unknown 3D pixel format");
|
||||
}
|
||||
|
||||
nlohmann::json frame_policy_schema(const Frame_Policy& policy) {
|
||||
const auto& current = policy.read_state<Frame_Policy::Base_Tag>();
|
||||
const Frame_Policy::State& frame_policy_common_state(
|
||||
const Frame_Policy& policy) noexcept {
|
||||
return policy.read_state<Frame_Policy::Base_Tag>();
|
||||
}
|
||||
|
||||
const Frame_Policy::State& frame_policy_common_state(
|
||||
const Frame_Policy_3D& policy) noexcept {
|
||||
return policy.read_state<Frame_Policy_3D::Base_Tag>().common;
|
||||
}
|
||||
|
||||
template <typename Policy>
|
||||
nlohmann::json frame_policy_schema(const Policy& policy) {
|
||||
const auto& current = frame_policy_common_state(policy);
|
||||
return {
|
||||
{"id", "frame-analysis"}, {"label", "渲染与媒体流水线"}, {"kind", "analysis"},
|
||||
{"fields", nlohmann::json::array({
|
||||
@@ -108,7 +119,8 @@ nlohmann::json frame_policy_schema(const Frame_Policy& policy) {
|
||||
};
|
||||
}
|
||||
|
||||
nlohmann::json write_frame_policy_prop(Frame_Policy& pacing,
|
||||
template <typename Policy>
|
||||
nlohmann::json write_frame_policy_prop(Policy& pacing,
|
||||
std::string_view key,
|
||||
const nlohmann::json& value) {
|
||||
if (key == "render_enabled" || key == "video_enabled") {
|
||||
@@ -462,7 +474,6 @@ struct Plot::Private {
|
||||
std::uint64_t next_frame_sequence{1};
|
||||
std::unique_ptr<Frame_Policy> frame_policy_2d{};
|
||||
std::unique_ptr<Frame_Policy_3D> frame_policy_3d{};
|
||||
Frame_Policy* frame_policy{}; /* 指向当前维度策略的公共事实层。 */
|
||||
Frame_Scheduler::Timer frame_timer{}; /* 每 Plot/Scene 只有轻量时间轮节点,不持有线程。 */
|
||||
static constexpr std::size_t scene_frame_capacity{3};
|
||||
std::array<Managed_Frame, scene_frame_capacity> frame_slots{}; /* Scene 与外接消费者共享生命周期的稳定三缓冲。 */
|
||||
@@ -506,7 +517,6 @@ struct Plot::Private {
|
||||
if (!built_policy)
|
||||
throw std::logic_error("2D Frame Policy dependency graph is invalid");
|
||||
frame_policy_2d = std::move(*built_policy);
|
||||
frame_policy = frame_policy_2d.get();
|
||||
}
|
||||
else {
|
||||
auto built_policy =
|
||||
@@ -514,9 +524,8 @@ struct Plot::Private {
|
||||
if (!built_policy)
|
||||
throw std::logic_error("3D Frame Policy dependency graph is invalid");
|
||||
frame_policy_3d = std::move(*built_policy);
|
||||
frame_policy = frame_policy_3d.get();
|
||||
frame_policy->set_mode(Frame_Pacing_Mode::maximum_rate);
|
||||
static_cast<void>(frame_policy->consume_events());
|
||||
frame_policy_3d->set_mode(Frame_Pacing_Mode::maximum_rate);
|
||||
static_cast<void>(frame_policy_3d->consume_events());
|
||||
}
|
||||
for (auto& slot : frame_slots) {
|
||||
if constexpr (std::same_as<Scene_Object, Scene_2D>)
|
||||
@@ -535,6 +544,34 @@ struct Plot::Private {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
decltype(auto) with_frame_policy(Callback&& callback) {
|
||||
if (frame_policy_2d)
|
||||
return std::forward<Callback>(callback)(*frame_policy_2d);
|
||||
return std::forward<Callback>(callback)(*frame_policy_3d);
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
decltype(auto) with_frame_policy(Callback&& callback) const {
|
||||
if (frame_policy_2d)
|
||||
return std::forward<Callback>(callback)(*frame_policy_2d);
|
||||
return std::forward<Callback>(callback)(*frame_policy_3d);
|
||||
}
|
||||
|
||||
[[nodiscard]] const Frame_Policy::State& pacing_state() const noexcept {
|
||||
if (frame_policy_2d)
|
||||
return frame_policy_2d->read_state<Frame_Policy::Base_Tag>();
|
||||
return frame_policy_3d->read_state<Frame_Policy_3D::Base_Tag>().common;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool consume_frame_policy_events() {
|
||||
const bool configuration_changed = with_frame_policy(
|
||||
[](auto& policy) { return policy.consume_events(); });
|
||||
if (frame_policy_3d)
|
||||
static_cast<void>(frame_policy_3d->consume_3d_events());
|
||||
return configuration_changed;
|
||||
}
|
||||
|
||||
[[nodiscard]] nlohmann::json schema() const;
|
||||
[[nodiscard]] Stream_Snapshot stream_snapshot() const;
|
||||
void publish(std::shared_ptr<const Plot_Stream_Frame> frame) noexcept;
|
||||
@@ -590,7 +627,8 @@ void Plot::Private::fail(std::exception_ptr failure) noexcept {
|
||||
|
||||
nlohmann::json Plot::Private::schema() const {
|
||||
auto result = view->schema();
|
||||
auto analysis = frame_policy_schema(*frame_policy);
|
||||
auto analysis = with_frame_policy(
|
||||
[](const auto& policy) { return frame_policy_schema(policy); });
|
||||
if (frame_policy_3d) {
|
||||
analysis["fields"].push_back({
|
||||
{"key", "pipeline_capacity"}, {"label", "3D 帧槽容量"},
|
||||
@@ -652,7 +690,7 @@ void Plot::Private::publish(
|
||||
void Plot::Private::refresh_schedule() {
|
||||
if (!frame_timer.valid()) return;
|
||||
const auto current_consumers = consumers.load(std::memory_order_acquire);
|
||||
const auto& pacing = frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& pacing = pacing_state();
|
||||
if (!pacing.render_enabled || current_consumers->empty()) {
|
||||
frame_timer.cancel();
|
||||
return;
|
||||
@@ -677,7 +715,9 @@ void Plot::Private::submit_tick_request(Plot_Render_Tick tick) {
|
||||
const auto issued_at = tick.issued_at;
|
||||
if (!tick_requests.enqueue(std::move(tick)))
|
||||
throw std::bad_alloc{};
|
||||
frame_policy->record_request(source, issued_at);
|
||||
with_frame_policy([&](auto& policy) {
|
||||
policy.record_request(source, issued_at);
|
||||
});
|
||||
tick_request_generation.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
@@ -694,7 +734,9 @@ void Plot::Private::keep_latest_tick(const Plot_Render_Tick& tick) {
|
||||
if (deferred_tick) {
|
||||
const auto current_priority = priority(deferred_tick->source);
|
||||
const auto next_priority = priority(tick.source);
|
||||
frame_policy->record_request_coalesced();
|
||||
with_frame_policy([](auto& policy) {
|
||||
policy.record_request_coalesced();
|
||||
});
|
||||
if (current_priority > next_priority ||
|
||||
(current_priority == next_priority &&
|
||||
deferred_tick->issued_at >= tick.issued_at))
|
||||
@@ -720,7 +762,7 @@ void Plot::Private::release_render_admission(std::weak_ptr<Plot> lifetime) {
|
||||
expected, Render_Admission_State::ready,
|
||||
std::memory_order_acq_rel, std::memory_order_acquire))
|
||||
return;
|
||||
const auto& pacing = frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& pacing = pacing_state();
|
||||
if (pacing.render_enabled && pacing.mode == Frame_Pacing_Mode::maximum_rate) {
|
||||
const auto current_consumers = consumers.load(std::memory_order_acquire);
|
||||
if (!current_consumers->empty()) {
|
||||
@@ -736,9 +778,7 @@ void Plot::Private::release_render_admission(std::weak_ptr<Plot> lifetime) {
|
||||
}
|
||||
|
||||
void Plot::Private::consume_tick(std::weak_ptr<Plot> lifetime) {
|
||||
if (frame_policy_3d)
|
||||
static_cast<void>(frame_policy_3d->consume_3d_events());
|
||||
if (frame_policy->consume_events()) refresh_schedule();
|
||||
if (consume_frame_policy_events()) refresh_schedule();
|
||||
const auto observed_generation =
|
||||
tick_request_generation.load(std::memory_order_acquire);
|
||||
Plot_Render_Tick requested;
|
||||
@@ -749,9 +789,7 @@ void Plot::Private::consume_tick(std::weak_ptr<Plot> lifetime) {
|
||||
auto tick = std::exchange(deferred_tick, {});
|
||||
if (tick) clock_tick(*tick);
|
||||
}
|
||||
if (frame_policy_3d)
|
||||
static_cast<void>(frame_policy_3d->consume_3d_events());
|
||||
if (frame_policy->consume_events()) refresh_schedule();
|
||||
if (consume_frame_policy_events()) refresh_schedule();
|
||||
tick_task_scheduled.store(false, std::memory_order_release);
|
||||
if (tick_request_generation.load(std::memory_order_acquire) !=
|
||||
observed_generation ||
|
||||
@@ -762,7 +800,7 @@ void Plot::Private::consume_tick(std::weak_ptr<Plot> lifetime) {
|
||||
|
||||
void Plot::Private::clock_tick(const Plot_Render_Tick& tick) {
|
||||
if (terminal_failure.load(std::memory_order_acquire)) return;
|
||||
const auto& pacing = frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& pacing = pacing_state();
|
||||
const bool accepted = pacing.render_enabled &&
|
||||
(tick.source == Frame_Request_Source::immediate ||
|
||||
(tick.source == Frame_Request_Source::periodic &&
|
||||
@@ -770,10 +808,14 @@ void Plot::Private::clock_tick(const Plot_Render_Tick& tick) {
|
||||
(tick.source == Frame_Request_Source::maximum_rate &&
|
||||
pacing.mode == Frame_Pacing_Mode::maximum_rate));
|
||||
if (!accepted) {
|
||||
frame_policy->record_policy_rejection();
|
||||
with_frame_policy([](auto& policy) {
|
||||
policy.record_policy_rejection();
|
||||
});
|
||||
return;
|
||||
}
|
||||
frame_policy->record_request_accepted(tick.source);
|
||||
with_frame_policy([&](auto& policy) {
|
||||
policy.record_request_accepted(tick.source);
|
||||
});
|
||||
render_frame(tick);
|
||||
}
|
||||
|
||||
@@ -848,7 +890,7 @@ nlohmann::json Plot::Private::trace_response(
|
||||
void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
if (terminal_failure.load(std::memory_order_acquire)) return;
|
||||
const auto streams = stream_snapshot();
|
||||
const auto& pacing = frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& pacing = pacing_state();
|
||||
if (!pacing.render_enabled || streams.consumers->empty()) return;
|
||||
|
||||
auto admission_expected = Render_Admission_State::ready;
|
||||
@@ -899,7 +941,9 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
break;
|
||||
}
|
||||
if (!managed) {
|
||||
frame_policy->record_frame_slot_backpressure();
|
||||
with_frame_policy([](auto& policy) {
|
||||
policy.record_frame_slot_backpressure();
|
||||
});
|
||||
keep_latest_tick(tick);
|
||||
/*
|
||||
* 三个槽都仍被外接消费者持有时,只保留 latest pending。这里绝不能
|
||||
@@ -937,8 +981,7 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
const Frame_Identity identity{
|
||||
sequence, tick.sequence == 0 ? sequence : tick.sequence};
|
||||
const auto request_source = tick.source;
|
||||
const auto policy_state =
|
||||
frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& policy_state = pacing_state();
|
||||
Render_Frame* logical_frame{};
|
||||
if (auto* frame_2d = std::get_if<std::unique_ptr<Frame_2D>>(&managed->frame)) {
|
||||
(*frame_2d)->begin(identity, Frame_2D::native_pixel_format,
|
||||
@@ -985,16 +1028,20 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
Size{static_cast<int>(tick.width), static_cast<int>(tick.height)});
|
||||
const auto result = (*scene_2d)->render(&output);
|
||||
if (!result) {
|
||||
frame_policy->record_scene_rejection();
|
||||
with_frame_policy([](auto& policy) {
|
||||
policy.record_scene_rejection();
|
||||
});
|
||||
rollback_unsubmitted();
|
||||
restore_taskflow_trace_claim();
|
||||
release_render_admission(lifetime);
|
||||
} else {
|
||||
taskflow_trace_claimed = false;
|
||||
managed->submitted_at = std::chrono::steady_clock::now();
|
||||
frame_policy->record_frame_submitted(
|
||||
sequence, request_source,
|
||||
managed->submitted_at - managed->tick_issued_at);
|
||||
with_frame_policy([&](auto& policy) {
|
||||
policy.record_frame_submitted(
|
||||
sequence, request_source,
|
||||
managed->submitted_at - managed->tick_issued_at);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1006,12 +1053,16 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
if (result == Render_Scene_3D::Render_Result::submitted) {
|
||||
taskflow_trace_claimed = false;
|
||||
managed->submitted_at = std::chrono::steady_clock::now();
|
||||
frame_policy->record_frame_submitted(
|
||||
sequence, request_source,
|
||||
managed->submitted_at - managed->tick_issued_at);
|
||||
with_frame_policy([&](auto& policy) {
|
||||
policy.record_frame_submitted(
|
||||
sequence, request_source,
|
||||
managed->submitted_at - managed->tick_issued_at);
|
||||
});
|
||||
return;
|
||||
}
|
||||
frame_policy->record_scene_rejection();
|
||||
with_frame_policy([](auto& policy) {
|
||||
policy.record_scene_rejection();
|
||||
});
|
||||
rollback_unsubmitted();
|
||||
restore_taskflow_trace_claim();
|
||||
release_render_admission(lifetime);
|
||||
@@ -1046,7 +1097,7 @@ void Plot::Private::publish_completed_frame(Render_Frame* completed) {
|
||||
if (!managed)
|
||||
throw std::logic_error("Scene completion graph has no rendering Plot frame");
|
||||
|
||||
const auto &pacing = frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& pacing = pacing_state();
|
||||
const auto identity = frame->identity();
|
||||
Frame_Identity rendered_identity = identity;
|
||||
std::shared_ptr<const std::vector<std::byte>> pixel_storage;
|
||||
@@ -1254,11 +1305,13 @@ void Plot::Private::finalize_retired_frame(Render_Frame* frame) {
|
||||
if (!managed)
|
||||
throw std::logic_error("retired frame has no owned Plot slot");
|
||||
|
||||
frame_policy->record_frame_completed(
|
||||
frame->identity().sequence,
|
||||
managed->submitted_at.time_since_epoch().count() == 0
|
||||
? std::chrono::steady_clock::duration::zero()
|
||||
: std::chrono::steady_clock::now() - managed->submitted_at);
|
||||
with_frame_policy([&](auto& policy) {
|
||||
policy.record_frame_completed(
|
||||
frame->identity().sequence,
|
||||
managed->submitted_at.time_since_epoch().count() == 0
|
||||
? std::chrono::steady_clock::duration::zero()
|
||||
: std::chrono::steady_clock::now() - managed->submitted_at);
|
||||
});
|
||||
|
||||
const auto statistics_generation_value =
|
||||
statistics_generation.load(std::memory_order_acquire);
|
||||
@@ -1526,7 +1579,9 @@ nlohmann::json Plot::write_prop(std::string_view component,
|
||||
ensure_started();
|
||||
if (component != "frame-analysis")
|
||||
return d->view->write_prop(component, key, value);
|
||||
auto result = write_frame_policy_prop(*d->frame_policy, key, value);
|
||||
auto result = d->with_frame_policy([&](auto& policy) {
|
||||
return write_frame_policy_prop(policy, key, value);
|
||||
});
|
||||
if (result.value("success", false))
|
||||
d->arm_tick_consumer(weak_from_this());
|
||||
return result;
|
||||
@@ -1592,8 +1647,7 @@ nlohmann::json Plot::diagnostics() const {
|
||||
? 1'000.0 / interval.trimmed_average : 0.0;
|
||||
}
|
||||
|
||||
const auto& pacing =
|
||||
d->frame_policy->read_state<Frame_Policy::Base_Tag>();
|
||||
const auto& pacing = d->pacing_state();
|
||||
const auto stream = d->stream_snapshot();
|
||||
const auto admission = d->render_admission.load(std::memory_order_acquire);
|
||||
const auto admission_name = [&] {
|
||||
@@ -1748,7 +1802,7 @@ nlohmann::json Plot::post_publish_taskflow_trace() const {
|
||||
void Plot::reset_diagnostics() {
|
||||
std::visit([](auto& scene) { scene->reset_diagnostics(); }, d->scene);
|
||||
d->statistics_generation.fetch_add(1, std::memory_order_acq_rel);
|
||||
d->frame_policy->reset_statistics();
|
||||
d->with_frame_policy([](auto& policy) { policy.reset_statistics(); });
|
||||
if (d->frame_policy_3d) d->frame_policy_3d->reset_3d_statistics();
|
||||
d->arm_tick_consumer(weak_from_this());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user