Files
Renderive/Core/architecture/Frame_Scheduler.cpp
T
2026-08-01 20:09:45 +08:00

279 lines
11 KiB
C++

#include "Frame_Scheduler.h"
#include <algorithm>
#include <cmath>
#include <utility>
namespace renderive {
namespace {
constexpr double Ratio_Threshold = 1.03;
constexpr int Switch_Samples = 3;
}
void Smoothed_Interval::update(double sample_ms) {
if (sample_ms <= 0.0 || !std::isfinite(sample_ms))
return;
if (!sample_count) {
srtt_ms = sample_ms;
jitter_ms = sample_ms * 0.5;
sample_count = 1;
return;
}
double error = std::abs(sample_ms - srtt_ms);
jitter_ms += 0.25 * (error - jitter_ms);
srtt_ms += 0.125 * (sample_ms - srtt_ms);
++sample_count;
}
double Smoothed_Interval::safe_ms(double jitter_factor) const {
return sample_count ? srtt_ms + jitter_factor * jitter_ms : 0.0;
}
bool Smoothed_Interval::valid() const {
return sample_count > 0;
}
void Frame_Lifecycle_Record::record_renderable_render(const std::string& object_name, const std::string& parent_object_name, int tree_depth, Renderable_Cache_Mode cache_mode, std::uint64_t render_time_ns) {
Renderable_Frame_Stat& stat = renderable_stat(object_name, parent_object_name, tree_depth);
stat.cache_enabled = cache_mode == Renderable_Cache_Mode::Local_Pixel;
++stat.render_count;
stat.total_render_time_ns += render_time_ns;
stat.last_total_render_time_ns = render_time_ns;
}
void Frame_Lifecycle_Record::record_renderable_self_draw(const std::string& object_name, const std::string& parent_object_name, int tree_depth, Renderable_Cache_Mode cache_mode, std::uint64_t draw_time_ns) {
Renderable_Frame_Stat& stat = renderable_stat(object_name, parent_object_name, tree_depth);
stat.cache_enabled = cache_mode == Renderable_Cache_Mode::Local_Pixel;
++stat.self_draw_count;
stat.self_draw_time_ns += draw_time_ns;
stat.last_self_draw_time_ns = draw_time_ns;
}
void Frame_Lifecycle_Record::record_renderable_cache_hit(const std::string& object_name, const std::string& parent_object_name, int tree_depth) {
++renderable_cache_hit_count;
++cache_stat(object_name, parent_object_name, tree_depth).hit_count;
}
void Frame_Lifecycle_Record::record_renderable_cache_miss(const std::string& object_name, const std::string& parent_object_name, int tree_depth) {
++renderable_cache_miss_count;
++cache_stat(object_name, parent_object_name, tree_depth).miss_count;
}
void Frame_Lifecycle_Record::record_renderable_cache_rebuild(const std::string& object_name, const std::string& parent_object_name, int tree_depth, std::uint64_t rebuild_time_ns, std::uint64_t image_bytes, std::uint64_t image_area, std::uint64_t edit_version, std::uint64_t ready_version) {
++renderable_cache_rebuild_count;
Renderable_Cache_Frame_Stat& stat = cache_stat(object_name, parent_object_name, tree_depth);
renderable_cache_rebuild_time_ns += rebuild_time_ns;
renderable_cache_image_bytes += image_bytes;
renderable_cache_image_area += image_area;
++stat.rebuild_count;
stat.rebuild_time_ns += rebuild_time_ns;
stat.last_rebuild_time_ns = rebuild_time_ns;
stat.last_image_bytes = image_bytes;
stat.last_image_area = image_area;
stat.last_edit_version = edit_version;
stat.last_ready_version = ready_version;
}
void Frame_Lifecycle_Record::record_renderable_cache_compose(const std::string& object_name, const std::string& parent_object_name, int tree_depth, std::uint64_t compose_time_ns, std::uint64_t image_bytes, std::uint64_t image_area, std::uint64_t edit_version, std::uint64_t ready_version) {
renderable_cache_compose_time_ns += compose_time_ns;
Renderable_Cache_Frame_Stat& stat = cache_stat(object_name, parent_object_name, tree_depth);
stat.compose_time_ns += compose_time_ns;
stat.last_compose_time_ns = compose_time_ns;
stat.last_image_bytes = image_bytes;
stat.last_image_area = image_area;
stat.last_edit_version = edit_version;
stat.last_ready_version = ready_version;
}
Renderable_Frame_Stat& Frame_Lifecycle_Record::renderable_stat(const std::string& object_name, const std::string& parent_object_name, int tree_depth) {
for (auto& stat : renderable_stats) {
if (stat.object_name == object_name && stat.parent_object_name == parent_object_name && stat.tree_depth == tree_depth)
return stat;
}
Renderable_Frame_Stat stat;
stat.object_name = object_name;
stat.parent_object_name = parent_object_name;
stat.tree_depth = tree_depth;
renderable_stats.push_back(std::move(stat));
return renderable_stats.back();
}
Renderable_Cache_Frame_Stat& Frame_Lifecycle_Record::cache_stat(const std::string& object_name, const std::string& parent_object_name, int tree_depth) {
for (auto& stat : renderable_cache_stats) {
if (stat.object_name == object_name && stat.parent_object_name == parent_object_name && stat.tree_depth == tree_depth)
return stat;
}
Renderable_Cache_Frame_Stat stat;
stat.object_name = object_name;
stat.parent_object_name = parent_object_name;
stat.tree_depth = tree_depth;
renderable_cache_stats.push_back(std::move(stat));
return renderable_cache_stats.back();
}
void Frame_Feedback_Controller::reset() {
std::uint64_t user_period = state_value.user_period_ns;
*this = Frame_Feedback_Controller();
state_value.user_period_ns = user_period;
refresh_control_snapshot();
}
void Frame_Feedback_Controller::set_max_render_fps(double fps) {
state_value.user_period_ns = fps > 0.0 ? ms_to_ns(1000.0 / fps) : 0;
refresh_control_snapshot();
}
void Frame_Feedback_Controller::on_input_changed(std::uint64_t, std::uint64_t input_version) {
if (input_version)
latest_input_version = input_version;
else
++latest_input_version;
}
void Frame_Feedback_Controller::on_render_started(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) {
last_render_begin_ns = now_ns;
frame.render_begin_ns = now_ns;
frame.wait_21_ns = now_ns > frame.render_role_enter_ns ? now_ns - frame.render_role_enter_ns : 0;
refresh_control_snapshot();
write_state(frame);
}
void Frame_Feedback_Controller::on_render_completed(Frame_Lifecycle_Record& frame) {
std::uint64_t render_duration_ns = frame.render_end_ns > frame.render_begin_ns ? frame.render_end_ns - frame.render_begin_ns : 0;
last_render_begin_ns = frame.render_begin_ns;
render_period.update(static_cast<double>(render_duration_ns + frame.wait_21_ns) / 1000000.0);
refresh_control_snapshot();
write_state(frame);
}
void Frame_Feedback_Controller::on_frame_presented(Frame_Lifecycle_Record& frame) {
std::uint64_t paint_duration_ns = frame.paint_end_ns > frame.paint_begin_ns ? frame.paint_end_ns - frame.paint_begin_ns : 0;
paint_period.update(static_cast<double>(paint_duration_ns + frame.wait_23_ns) / 1000000.0);
frame.outcome = Frame_Outcome::Presented;
refresh_control_snapshot();
write_state(frame);
}
void Frame_Feedback_Controller::on_frame_superseded(Frame_Lifecycle_Record& frame) {
frame.outcome = Frame_Outcome::Superseded;
refresh_control_snapshot();
write_state(frame);
}
void Frame_Feedback_Controller::on_frame_dropped(Frame_Lifecycle_Record& frame, Frame_Outcome outcome) {
frame.outcome = outcome;
refresh_control_snapshot();
write_state(frame);
}
bool Frame_Feedback_Controller::rate_limit_ready(std::uint64_t now_ns) const {
return now_ns >= next_render_time;
}
std::uint64_t Frame_Feedback_Controller::next_render_time_ns() const {
return next_render_time;
}
const Refresh_Control_Snapshot& Frame_Feedback_Controller::state() const {
return state_value;
}
void Frame_Feedback_Controller::write_state(Frame_Lifecycle_Record& frame) const {
frame.refresh = state_value;
}
void Frame_Feedback_Controller::refresh_control_snapshot() {
std::uint64_t render_period_ns = render_period.valid() ? ms_to_ns(render_period.safe_ms(0.5)) : 0;
std::uint64_t paint_period_ns = paint_period.valid() ? ms_to_ns(paint_period.safe_ms(0.5)) : 0;
std::uint64_t user_period_ns = state_value.user_period_ns;
Refresh_Limit_Source candidate = classify_limit_source(render_period_ns, paint_period_ns, user_period_ns);
std::uint64_t candidate_period = 0;
switch (candidate) {
case Refresh_Limit_Source::Render:
candidate_period = render_period_ns;
break;
case Refresh_Limit_Source::Paint:
candidate_period = paint_period_ns;
break;
case Refresh_Limit_Source::User:
candidate_period = user_period_ns;
break;
case Refresh_Limit_Source::Unknown:
break;
}
update_limit_source(candidate, candidate_period);
std::uint64_t target_period = std::max({render_period_ns, paint_period_ns, user_period_ns});
state_value.render_period_ns = render_period_ns;
state_value.paint_period_ns = paint_period_ns;
state_value.target_period_ns = target_period;
if (state_value.source == Refresh_Limit_Source::Unknown)
state_value.source = candidate;
if (state_value.source == Refresh_Limit_Source::User && last_render_begin_ns)
next_render_time = last_render_begin_ns + user_period_ns;
else
next_render_time = 0;
}
Refresh_Limit_Source Frame_Feedback_Controller::classify_limit_source(std::uint64_t render_period_ns, std::uint64_t paint_period_ns, std::uint64_t user_period_ns) const {
if (!render_period_ns && !paint_period_ns)
return user_period_ns ? Refresh_Limit_Source::User : Refresh_Limit_Source::Unknown;
std::uint64_t max_period = std::max({render_period_ns, paint_period_ns, user_period_ns});
if (max_period == user_period_ns && user_period_ns)
return Refresh_Limit_Source::User;
if (max_period == paint_period_ns && paint_period_ns)
return Refresh_Limit_Source::Paint;
if (max_period == render_period_ns && render_period_ns)
return Refresh_Limit_Source::Render;
return Refresh_Limit_Source::Unknown;
}
void Frame_Feedback_Controller::update_limit_source(Refresh_Limit_Source candidate, std::uint64_t candidate_period_ns) {
if (candidate == Refresh_Limit_Source::Unknown)
return;
if (state_value.source == Refresh_Limit_Source::Unknown) {
state_value.source = candidate;
candidate_source = candidate;
candidate_count = 0;
return;
}
std::uint64_t current_period = 0;
switch (state_value.source) {
case Refresh_Limit_Source::Render:
current_period = state_value.render_period_ns;
break;
case Refresh_Limit_Source::Paint:
current_period = state_value.paint_period_ns;
break;
case Refresh_Limit_Source::User:
current_period = state_value.user_period_ns;
break;
case Refresh_Limit_Source::Unknown:
break;
}
if (candidate == state_value.source || (current_period && static_cast<double>(candidate_period_ns) <= static_cast<double>(current_period) * Ratio_Threshold)) {
candidate_source = candidate;
candidate_count = 0;
return;
}
if (candidate != candidate_source) {
candidate_source = candidate;
candidate_count = 1;
return;
}
if (++candidate_count >= Switch_Samples) {
state_value.source = candidate;
candidate_count = 0;
}
}
double Frame_Feedback_Controller::duration_ms(std::uint64_t begin_ns, std::uint64_t end_ns) {
return end_ns > begin_ns ? static_cast<double>(end_ns - begin_ns) / 1000000.0 : 0.0;
}
std::uint64_t Frame_Feedback_Controller::ms_to_ns(double ms) {
if (ms <= 0.0 || !std::isfinite(ms))
return 0;
return static_cast<std::uint64_t>(ms * 1000000.0);
}
} // namespace renderive