修复崩溃

This commit is contained in:
2026-08-28 01:36:41 +08:00
parent fc68247cbc
commit 144602ef45
5 changed files with 400 additions and 49 deletions
@@ -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);
};
}
+42 -1
View File
@@ -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();