名称优化
This commit is contained in:
@@ -0,0 +1,476 @@
|
||||
#include "Performance_Overlay_p.h"
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <taskflow/taskflow.hpp>
|
||||
#include "../base/Task.h"
|
||||
#include "../plot/Plot_Core.h"
|
||||
namespace renderive {
|
||||
namespace {
|
||||
class Performance_Overlay_Service {
|
||||
public:
|
||||
~Performance_Overlay_Service() {
|
||||
shutdown();
|
||||
}
|
||||
bool try_submit(Task task) {
|
||||
if (!task || shutting_down.load(std::memory_order_acquire))
|
||||
return false;
|
||||
auto topology = std::make_shared<tf::Taskflow>();
|
||||
auto task_ptr = std::make_shared<Task>(std::move(task));
|
||||
topology->emplace([task_ptr]() {
|
||||
(*task_ptr)();
|
||||
});
|
||||
executor.run(*topology, [topology]() {});
|
||||
return true;
|
||||
}
|
||||
void shutdown() {
|
||||
if (shutting_down.exchange(true, std::memory_order_acq_rel))
|
||||
return;
|
||||
executor.wait_for_all();
|
||||
}
|
||||
private:
|
||||
tf::Executor executor{1};
|
||||
std::atomic_bool shutting_down{false};
|
||||
};
|
||||
Performance_Overlay_Service& performance_background_service() {
|
||||
static Performance_Overlay_Service service;
|
||||
return service;
|
||||
}
|
||||
double 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;
|
||||
}
|
||||
double cache_ratio(std::uint64_t hit, std::uint64_t miss) {
|
||||
std::uint64_t total = hit + miss;
|
||||
return total ? static_cast<double>(hit) / static_cast<double>(total) : 0.0;
|
||||
}
|
||||
std::string source_text(Refresh_Limit_Source source) {
|
||||
switch (source) {
|
||||
case Refresh_Limit_Source::Unknown:
|
||||
return "unknown";
|
||||
case Refresh_Limit_Source::Render:
|
||||
return "render";
|
||||
case Refresh_Limit_Source::Paint:
|
||||
return "paint";
|
||||
case Refresh_Limit_Source::User:
|
||||
return "user";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
std::string outcome_text(Frame_Outcome outcome) {
|
||||
switch (outcome) {
|
||||
case Frame_Outcome::Presented:
|
||||
return "presented";
|
||||
case Frame_Outcome::Superseded:
|
||||
return "superseded";
|
||||
case Frame_Outcome::Render_Acquire_Dropped:
|
||||
return "render_acquire_dropped";
|
||||
case Frame_Outcome::Publish_Dropped:
|
||||
return "publish_dropped";
|
||||
case Frame_Outcome::Paint_Acquire_Dropped:
|
||||
return "paint_acquire_dropped";
|
||||
case Frame_Outcome::Worker_Budget_Dropped:
|
||||
return "worker_budget_dropped";
|
||||
case Frame_Outcome::Cancelled:
|
||||
return "cancelled";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
std::string csv_escape(const std::string& value) {
|
||||
std::string escaped;
|
||||
escaped.reserve(value.size() + 2);
|
||||
escaped.push_back('"');
|
||||
for (char ch : value) {
|
||||
if (ch == '"')
|
||||
escaped.push_back('"');
|
||||
escaped.push_back(ch);
|
||||
}
|
||||
escaped.push_back('"');
|
||||
return escaped;
|
||||
}
|
||||
std::string renderable_cache_stats_text(const Frame_Lifecycle_Record& frame) {
|
||||
std::ostringstream stream;
|
||||
bool first = true;
|
||||
for (const auto& stat : frame.renderable_cache_stats) {
|
||||
if (!first)
|
||||
stream << '|';
|
||||
first = false;
|
||||
stream << stat.object_name
|
||||
<< ":parent=" << stat.parent_object_name
|
||||
<< ";depth=" << stat.tree_depth
|
||||
<< ";hit=" << stat.hit_count
|
||||
<< ";miss=" << stat.miss_count
|
||||
<< ";rebuild=" << stat.rebuild_count
|
||||
<< ";rebuild_ns=" << stat.rebuild_time_ns
|
||||
<< ";compose_ns=" << stat.compose_time_ns
|
||||
<< ";subtree_compose_ns=" << renderable_cache_subtree_compose_time(frame.renderable_cache_stats, stat)
|
||||
<< ";last_rebuild_ns=" << stat.last_rebuild_time_ns
|
||||
<< ";last_compose_ns=" << stat.last_compose_time_ns
|
||||
<< ";image_bytes=" << stat.last_image_bytes
|
||||
<< ";image_area=" << stat.last_image_area
|
||||
<< ";version=" << stat.last_edit_version << '/' << stat.last_ready_version;
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
std::string renderable_stats_text(const Frame_Lifecycle_Record& frame) {
|
||||
std::ostringstream stream;
|
||||
bool first = true;
|
||||
for (const auto& stat : frame.renderable_stats) {
|
||||
if (!first)
|
||||
stream << '|';
|
||||
first = false;
|
||||
stream << stat.object_name
|
||||
<< ":parent=" << stat.parent_object_name
|
||||
<< ";depth=" << stat.tree_depth
|
||||
<< ";cache=" << (stat.cache_enabled ? 1 : 0)
|
||||
<< ";render=" << stat.render_count
|
||||
<< ";self_draw=" << stat.self_draw_count
|
||||
<< ";render_ns=" << stat.total_render_time_ns
|
||||
<< ";self_draw_ns=" << stat.self_draw_time_ns
|
||||
<< ";last_render_ns=" << stat.last_total_render_time_ns
|
||||
<< ";last_self_draw_ns=" << stat.last_self_draw_time_ns;
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
class Frame_Performance_Logger {
|
||||
public:
|
||||
bool is_enabled() const {
|
||||
return enabled();
|
||||
}
|
||||
void write(const Frame_Lifecycle_Record& frame) {
|
||||
if (!enabled())
|
||||
return;
|
||||
const Refresh_Control_Snapshot& refresh = frame.refresh;
|
||||
std::ostringstream stream;
|
||||
stream << std::setprecision(9)
|
||||
<< frame.frame_id << ','
|
||||
<< frame.input_version << ','
|
||||
<< outcome_text(frame.outcome) << ','
|
||||
<< source_text(refresh.source) << ','
|
||||
<< refresh.render_period_ns << ','
|
||||
<< refresh.paint_period_ns << ','
|
||||
<< refresh.user_period_ns << ','
|
||||
<< refresh.target_period_ns << ','
|
||||
<< duration_ms(frame.render_request_ns, frame.render_begin_ns) << ','
|
||||
<< duration_ms(frame.prepare_begin_ns, frame.prepare_end_ns) << ','
|
||||
<< duration_ms(frame.draw_begin_ns, frame.draw_end_ns) << ','
|
||||
<< duration_ms(frame.render_begin_ns, frame.render_end_ns) << ','
|
||||
<< duration_ms(frame.transition_12_ns, frame.paint_begin_ns ? frame.paint_begin_ns : frame.superseded_time_ns) << ','
|
||||
<< duration_ms(frame.update_request_time_ns, frame.paint_begin_ns) << ','
|
||||
<< duration_ms(frame.paint_begin_ns, frame.paint_end_ns) << ','
|
||||
<< duration_ms(frame.core_data_time_ns, frame.paint_end_ns) << ','
|
||||
<< frame.wait_21_ns << ','
|
||||
<< frame.wait_12_ns << ','
|
||||
<< frame.wait_23_ns << ','
|
||||
<< frame.wait_32_ns << ','
|
||||
<< frame.raw_input_count << ','
|
||||
<< frame.prepared_output_count << ','
|
||||
<< frame.renderable_cache_hit_count << ','
|
||||
<< frame.renderable_cache_miss_count << ','
|
||||
<< frame.renderable_cache_rebuild_count << ','
|
||||
<< frame.renderable_cache_rebuild_time_ns << ','
|
||||
<< frame.renderable_cache_compose_time_ns << ','
|
||||
<< frame.renderable_cache_image_bytes << ','
|
||||
<< frame.renderable_cache_image_area << ','
|
||||
<< cache_ratio(frame.renderable_cache_hit_count, frame.renderable_cache_miss_count) << ','
|
||||
<< frame.pixel_width << ','
|
||||
<< frame.pixel_height << ','
|
||||
<< frame.core_data_time_ns << ','
|
||||
<< frame.render_begin_ns << ','
|
||||
<< frame.render_end_ns << ','
|
||||
<< frame.transition_12_ns << ','
|
||||
<< frame.transition_23_ns << ','
|
||||
<< frame.paint_begin_ns << ','
|
||||
<< frame.paint_end_ns << ','
|
||||
<< frame.transition_32_ns << ','
|
||||
<< frame.superseded_time_ns << ','
|
||||
<< csv_escape(renderable_stats_text(frame)) << ','
|
||||
<< csv_escape(renderable_cache_stats_text(frame));
|
||||
csv_log(path(), header(), stream.str());
|
||||
}
|
||||
private:
|
||||
static bool enabled() {
|
||||
static bool value = []() {
|
||||
const char* env = std::getenv("RENDERIVE_PERFORMANCE_LOG");
|
||||
return env && std::atoi(env) != 0;
|
||||
}();
|
||||
return value;
|
||||
}
|
||||
static std::string path() {
|
||||
static std::string value = []() {
|
||||
const char* configured = std::getenv("RENDERIVE_PERFORMANCE_LOG_PATH");
|
||||
if (!configured || configured[0] == '\0') {
|
||||
return (std::filesystem::current_path() / "renderive_performance_log.csv").string();
|
||||
}
|
||||
return std::string(configured);
|
||||
}();
|
||||
return value;
|
||||
}
|
||||
static std::string header() {
|
||||
return "frame_id,input_version,outcome,limit_source,render_period_ns,paint_period_ns,user_period_ns,target_period_ns,request_wait_ms,prepare_ms,draw_ms,render_ms,middle_wait_ms,update_wait_ms,paint_ms,frame_age_ms,wait_21_ns,wait_12_ns,wait_23_ns,wait_32_ns,raw_input_count,prepared_output_count,cache_hit_count,cache_miss_count,cache_rebuild_count,cache_rebuild_time_ns,cache_compose_time_ns,cache_image_bytes,cache_image_area,cache_hit_ratio,pixel_width,pixel_height,core_data_time_ns,render_begin_time_ns,render_end_time_ns,transition_12_ns,transition_23_ns,paint_begin_time_ns,paint_end_time_ns,transition_32_ns,superseded_time_ns,renderable_stats,renderable_cache_stats";
|
||||
}
|
||||
};
|
||||
Frame_Performance_Logger& frame_log() {
|
||||
static Frame_Performance_Logger value;
|
||||
return value;
|
||||
}
|
||||
static std::shared_ptr<Performance_Overlay> performance_overlay_owner(const std::shared_ptr<Plot_Render_Context>& ctx) {
|
||||
if (!ctx)
|
||||
return {};
|
||||
auto observer = std::atomic_load_explicit(&ctx->frame_lifecycle_observer, std::memory_order_acquire);
|
||||
return std::dynamic_pointer_cast<Performance_Overlay>(observer);
|
||||
}
|
||||
static std::function<void()> performance_update_callback(const std::shared_ptr<Plot_Render_Context>& ctx) {
|
||||
if (!ctx)
|
||||
return {};
|
||||
std::lock_guard<std::mutex> lock(ctx->performance_update_callback_mutex);
|
||||
return ctx->performance_update_callback;
|
||||
}
|
||||
static void request_performance_update(const std::shared_ptr<Performance_Worker_State>& worker) {
|
||||
if (!worker)
|
||||
return;
|
||||
std::function<void()> callback;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(worker->update_callback_mutex);
|
||||
callback = worker->update_callback;
|
||||
}
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
}
|
||||
void schedule_performance_metrics_worker(const std::shared_ptr<Performance_Worker_State>& state) {
|
||||
if (!state || state->cancelled.load(std::memory_order_acquire))
|
||||
return;
|
||||
state->work_requested.store(true, std::memory_order_release);
|
||||
if (state->worker_active.exchange(true, std::memory_order_acq_rel))
|
||||
return;
|
||||
if (!performance_background_service().try_submit(Task([state]() {
|
||||
Performance_Overlay_Private::run_metrics_worker(state);
|
||||
}))) {
|
||||
state->worker_active.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
Performance_Overlay::Performance_Overlay()
|
||||
: private_data(std::make_unique<Performance_Overlay_Private>()) {}
|
||||
Performance_Overlay::~Performance_Overlay() = default;
|
||||
Performance_Overlay_Private* Performance_Overlay::d() {
|
||||
return private_data.get();
|
||||
}
|
||||
static std::shared_ptr<Performance_Overlay> attach_performance_overlay_impl(Plot_Core& plot, const Performance_Overlay_Options* options) {
|
||||
auto ctx = plot.render_context();
|
||||
if (!ctx)
|
||||
return {};
|
||||
auto shower = performance_overlay_owner(ctx);
|
||||
if (shower) {
|
||||
if (options)
|
||||
shower->set_options(*options);
|
||||
return shower;
|
||||
}
|
||||
auto created_shower = renderive::make_shared<Performance_Overlay>();
|
||||
if (options)
|
||||
created_shower->set_options(*options);
|
||||
created_shower->set_update_callback(performance_update_callback(ctx));
|
||||
std::atomic_store_explicit(&ctx->frame_lifecycle_observer, std::static_pointer_cast<Frame_Lifecycle_Observer>(created_shower), std::memory_order_release);
|
||||
return created_shower;
|
||||
}
|
||||
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Plot_Core& plot) {
|
||||
return attach_performance_overlay_impl(plot, nullptr);
|
||||
}
|
||||
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Plot_Core& plot, const Performance_Overlay_Options& options) {
|
||||
return attach_performance_overlay_impl(plot, &options);
|
||||
}
|
||||
std::shared_ptr<Performance_Overlay> performance_overlay(Plot_Core& plot) {
|
||||
return performance_overlay_owner(plot.render_context());
|
||||
}
|
||||
void detach_performance_overlay(Plot_Core& plot) {
|
||||
auto ctx = plot.render_context();
|
||||
if (!ctx)
|
||||
return;
|
||||
auto shower = performance_overlay_owner(ctx);
|
||||
std::atomic_store_explicit(&ctx->frame_lifecycle_observer, std::shared_ptr<Frame_Lifecycle_Observer>{}, std::memory_order_release);
|
||||
if (shower)
|
||||
shower->set_enabled(false);
|
||||
}
|
||||
bool performance_overlay_enabled(const Plot_Core& plot) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
return shower && shower->enabled();
|
||||
}
|
||||
void set_performance_overlay_enabled(Plot_Core& plot, bool enabled) {
|
||||
if (!enabled) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
if (shower)
|
||||
shower->set_enabled(false);
|
||||
return;
|
||||
}
|
||||
auto shower = attach_performance_overlay(plot);
|
||||
if (shower) {
|
||||
shower->set_enabled(true);
|
||||
shower->set_viewport(plot.viewport_size(), plot.render_context() ? plot.render_context()->viewport_version.load(std::memory_order_acquire) : 0);
|
||||
}
|
||||
}
|
||||
std::shared_ptr<const Performance_Display_Snapshot> performance_overlay_snapshot(const Plot_Core& plot) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
if (!shower || !shower->enabled())
|
||||
return {};
|
||||
return shower->display_snapshot();
|
||||
}
|
||||
void set_performance_overlay_update_callback(Plot_Core& plot, std::function<void()> callback) {
|
||||
auto ctx = plot.render_context();
|
||||
if (!ctx)
|
||||
return;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(ctx->performance_update_callback_mutex);
|
||||
ctx->performance_update_callback = std::move(callback);
|
||||
}
|
||||
auto shower = performance_overlay_owner(ctx);
|
||||
if (shower)
|
||||
shower->set_update_callback(performance_update_callback(ctx));
|
||||
}
|
||||
void notify_performance_overlay_viewport_changed(Plot_Core& plot) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
if (!shower || !shower->enabled())
|
||||
return;
|
||||
auto ctx = plot.render_context();
|
||||
shower->set_viewport(plot.viewport_size(), ctx ? ctx->viewport_version.load(std::memory_order_acquire) : 0);
|
||||
}
|
||||
bool performance_overlay_handle_wheel(Plot_Core& plot, PointF position, double angle_delta_y, double pixel_delta_y) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
return shower && shower->enabled() && shower->handle_wheel(position, angle_delta_y, pixel_delta_y);
|
||||
}
|
||||
bool performance_overlay_handle_pointer_press(Plot_Core& plot, PointF position) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
return shower && shower->enabled() && shower->handle_pointer_press(position);
|
||||
}
|
||||
bool performance_overlay_handle_pointer_move(Plot_Core& plot, PointF position) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
return shower && shower->enabled() && shower->handle_pointer_move(position);
|
||||
}
|
||||
bool performance_overlay_handle_pointer_release(Plot_Core& plot, PointF position) {
|
||||
auto shower = performance_overlay_owner(plot.render_context());
|
||||
return shower && shower->enabled() && shower->handle_pointer_release(position);
|
||||
}
|
||||
void Performance_Overlay::set_enabled(bool enabled) {
|
||||
auto worker = d()->metrics_worker;
|
||||
d()->enabled_value.store(enabled, std::memory_order_release);
|
||||
worker->enabled.store(enabled, std::memory_order_release);
|
||||
if (!enabled) {
|
||||
worker->published_snapshot.store({}, std::memory_order_release);
|
||||
request_performance_update(worker);
|
||||
return;
|
||||
}
|
||||
worker->reset_requested.store(true, std::memory_order_release);
|
||||
worker->snapshot_rebuild_requested.store(true, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(worker);
|
||||
}
|
||||
bool Performance_Overlay::enabled() const {
|
||||
return const_cast<Performance_Overlay*>(this)->d()->enabled_value.load(std::memory_order_acquire);
|
||||
}
|
||||
std::shared_ptr<const Performance_Display_Snapshot> Performance_Overlay::display_snapshot() const {
|
||||
return const_cast<Performance_Overlay*>(this)->d()->metrics_worker->published_snapshot.load(std::memory_order_acquire);
|
||||
}
|
||||
void Performance_Overlay::set_options(Performance_Overlay_Options options) {
|
||||
auto worker = d()->metrics_worker;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(worker->options_mutex);
|
||||
worker->options = std::move(options);
|
||||
}
|
||||
worker->snapshot_rebuild_requested.store(true, std::memory_order_release);
|
||||
if (enabled())
|
||||
schedule_performance_metrics_worker(worker);
|
||||
}
|
||||
Performance_Overlay_Options Performance_Overlay::options() const {
|
||||
auto worker = const_cast<Performance_Overlay*>(this)->d()->metrics_worker;
|
||||
std::lock_guard<std::mutex> lock(worker->options_mutex);
|
||||
return worker->options;
|
||||
}
|
||||
void Performance_Overlay::set_font(Font font) {
|
||||
Performance_Overlay_Options value = options();
|
||||
value.font = std::move(font);
|
||||
set_options(std::move(value));
|
||||
}
|
||||
void Performance_Overlay::set_foreground(Color color) {
|
||||
Performance_Overlay_Options value = options();
|
||||
value.foreground = color;
|
||||
set_options(std::move(value));
|
||||
}
|
||||
void Performance_Overlay::set_background(Color color) {
|
||||
Performance_Overlay_Options value = options();
|
||||
value.background = color;
|
||||
set_options(std::move(value));
|
||||
}
|
||||
void Performance_Overlay::collect_frame(std::shared_ptr<const Frame_Lifecycle_Record> frame) {
|
||||
if (!enabled())
|
||||
return;
|
||||
d()->enqueue_frame_metric(frame);
|
||||
write_frame_performance_log(frame);
|
||||
}
|
||||
void Performance_Overlay::set_viewport(Size viewport, std::uint64_t version) {
|
||||
d()->metrics_worker->viewport_width.store(viewport.width, std::memory_order_release);
|
||||
d()->metrics_worker->viewport_height.store(viewport.height, std::memory_order_release);
|
||||
d()->metrics_worker->viewport_version.store(version, std::memory_order_release);
|
||||
d()->metrics_worker->snapshot_rebuild_requested.store(true, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
}
|
||||
bool Performance_Overlay::handle_wheel(PointF position, double angle_delta_y, double pixel_delta_y) {
|
||||
auto snapshot = display_snapshot();
|
||||
if (!snapshot || !snapshot->destination_rect.contains(position))
|
||||
return false;
|
||||
double line_height = snapshot->line_height > 0.0 ? snapshot->line_height : d()->metrics_worker->last_line_height;
|
||||
double delta = 0.0;
|
||||
if (pixel_delta_y != 0.0)
|
||||
delta = -pixel_delta_y;
|
||||
else if (angle_delta_y != 0.0)
|
||||
delta = -(angle_delta_y / 120.0) * 3.0 * std::max(1.0, line_height);
|
||||
if (delta != 0.0) {
|
||||
performance_atomic_add(d()->metrics_worker->pending_wheel_delta_px, delta);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Performance_Overlay::handle_pointer_press(PointF position) {
|
||||
auto snapshot = display_snapshot();
|
||||
if (!snapshot || snapshot->max_scroll_offset <= 0.0)
|
||||
return false;
|
||||
if (snapshot->scroll_thumb_rect.contains(position)) {
|
||||
d()->metrics_worker->drag_anchor_y.store(position.y - snapshot->scroll_thumb_rect.y, std::memory_order_release);
|
||||
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
||||
d()->metrics_worker->drag_end_requested.store(false, std::memory_order_release);
|
||||
d()->metrics_worker->drag_active.store(true, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
}
|
||||
if (snapshot->scroll_track_rect.contains(position)) {
|
||||
int page = position.y < snapshot->scroll_thumb_rect.y ? -1 : 1;
|
||||
d()->metrics_worker->pending_page_delta.fetch_add(page, std::memory_order_acq_rel);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Performance_Overlay::handle_pointer_move(PointF position) {
|
||||
if (!d()->metrics_worker->drag_active.load(std::memory_order_acquire))
|
||||
return false;
|
||||
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
}
|
||||
bool Performance_Overlay::handle_pointer_release(PointF position) {
|
||||
if (!d()->metrics_worker->drag_active.load(std::memory_order_acquire))
|
||||
return false;
|
||||
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
||||
d()->metrics_worker->drag_end_requested.store(true, std::memory_order_release);
|
||||
schedule_performance_metrics_worker(d()->metrics_worker);
|
||||
return true;
|
||||
}
|
||||
void Performance_Overlay::set_update_callback(std::function<void()> callback) {
|
||||
std::lock_guard<std::mutex> lock(d()->metrics_worker->update_callback_mutex);
|
||||
d()->metrics_worker->update_callback = std::move(callback);
|
||||
}
|
||||
void write_frame_performance_log(std::shared_ptr<const Frame_Lifecycle_Record> frame) {
|
||||
if (!frame || !frame_log().is_enabled())
|
||||
return;
|
||||
performance_background_service().try_submit(Task([frame = std::move(frame)]() {
|
||||
frame_log().write(*frame);
|
||||
}));
|
||||
}
|
||||
} // namespace renderive
|
||||
Reference in New Issue
Block a user