521 lines
22 KiB
C++
521 lines
22 KiB
C++
#include "Performance_Overlay_p.h"
|
|
#include "../architecture/Plot_Render_Context.h"
|
|
#include "../performance/Performance_Log_Service.h"
|
|
#include "../plot/Plot_Core_Access.h"
|
|
#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::Input:
|
|
return "input";
|
|
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 bottleneck_text(Low_Latency_Bottleneck_State state) {
|
|
switch (state) {
|
|
case Low_Latency_Bottleneck_State::Unknown:
|
|
return "unknown";
|
|
case Low_Latency_Bottleneck_State::Input_Limited:
|
|
return "input";
|
|
case Low_Latency_Bottleneck_State::Producer_Limited:
|
|
return "producer";
|
|
case Low_Latency_Bottleneck_State::Consumer_Limited:
|
|
return "consumer";
|
|
case Low_Latency_Bottleneck_State::User_Limited:
|
|
return "user";
|
|
}
|
|
return "unknown";
|
|
}
|
|
std::string production_mode_text(Low_Latency_Production_Mode mode) {
|
|
switch (mode) {
|
|
case Low_Latency_Production_Mode::Latency_Eager:
|
|
return "latency_eager";
|
|
case Low_Latency_Production_Mode::Consumer_Paced:
|
|
return "consumer_paced";
|
|
}
|
|
return "latency_eager";
|
|
}
|
|
std::string limit_reason_text(Low_Latency_Limit_Reason reason) {
|
|
switch (reason) {
|
|
case Low_Latency_Limit_Reason::User_Fps:
|
|
return "user_fps";
|
|
case Low_Latency_Limit_Reason::Consumer_Capacity:
|
|
return "consumer_capacity";
|
|
case Low_Latency_Limit_Reason::Render_Capacity:
|
|
return "render_capacity";
|
|
case Low_Latency_Limit_Reason::No_Work:
|
|
return "no_work";
|
|
case Low_Latency_Limit_Reason::Shutdown:
|
|
return "shutdown";
|
|
}
|
|
return "user_fps";
|
|
}
|
|
std::string consumer_confidence_text(Consumer_Confidence confidence) {
|
|
switch (confidence) {
|
|
case Consumer_Confidence::Low:
|
|
return "low";
|
|
case Consumer_Confidence::Medium:
|
|
return "medium";
|
|
case Consumer_Confidence::High:
|
|
return "high";
|
|
}
|
|
return "low";
|
|
}
|
|
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 Performance_Log_Plot_Identity performance_plot_identity(const std::shared_ptr<Plot_Render_Context>& ctx) {
|
|
Performance_Log_Plot_Identity identity;
|
|
if (!ctx)
|
|
return identity;
|
|
identity.plot_id = ctx->performance_plot_id.load(std::memory_order_acquire);
|
|
{
|
|
std::lock_guard<std::mutex> lock(ctx->performance_plot_name_mutex);
|
|
identity.plot_name = ctx->performance_plot_name;
|
|
}
|
|
if (identity.plot_name.empty())
|
|
identity.plot_name = "plot_" + std::to_string(identity.plot_id);
|
|
return identity;
|
|
}
|
|
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 std::function<void(std::string)> performance_clipboard_callback(const std::shared_ptr<Plot_Render_Context>& ctx) {
|
|
if (!ctx)
|
|
return {};
|
|
std::lock_guard<std::mutex> lock(ctx->performance_clipboard_callback_mutex);
|
|
return ctx->performance_clipboard_callback;
|
|
}
|
|
static std::string performance_snapshot_text(const Performance_Display_Snapshot& snapshot) {
|
|
std::size_t size = 0;
|
|
for (const auto& line : snapshot.lines)
|
|
size += line.size() + 1;
|
|
std::string text;
|
|
text.reserve(size);
|
|
for (std::size_t i = 0; i < snapshot.lines.size(); ++i) {
|
|
if (i)
|
|
text.push_back('\n');
|
|
text += snapshot.lines[i];
|
|
}
|
|
return text;
|
|
}
|
|
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, bool request_repaint) {
|
|
if (!state || state->cancelled.load(std::memory_order_acquire))
|
|
return;
|
|
if (request_repaint)
|
|
state->repaint_requested.store(true, std::memory_order_release);
|
|
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_Core_Context_Access::render_context(plot);
|
|
if (!ctx)
|
|
return {};
|
|
auto shower = performance_overlay_owner(ctx);
|
|
if (shower) {
|
|
shower->set_log_identity(performance_plot_identity(ctx));
|
|
if (options)
|
|
shower->set_options(*options);
|
|
return shower;
|
|
}
|
|
auto created_shower = renderive::make_shared<Performance_Overlay>();
|
|
created_shower->set_log_identity(performance_plot_identity(ctx));
|
|
if (options)
|
|
created_shower->set_options(*options);
|
|
created_shower->set_update_callback(performance_update_callback(ctx));
|
|
created_shower->set_clipboard_callback(performance_clipboard_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_Core_Context_Access::render_context(plot));
|
|
}
|
|
void set_performance_plot_name(Plot_Core& plot, std::string name) {
|
|
auto ctx = Plot_Core_Context_Access::render_context(plot);
|
|
if (!ctx)
|
|
return;
|
|
{
|
|
std::lock_guard<std::mutex> lock(ctx->performance_plot_name_mutex);
|
|
ctx->performance_plot_name = std::move(name);
|
|
}
|
|
auto shower = performance_overlay_owner(ctx);
|
|
if (shower)
|
|
shower->set_log_identity(performance_plot_identity(ctx));
|
|
}
|
|
void detach_performance_overlay(Plot_Core& plot) {
|
|
auto ctx = Plot_Core_Context_Access::render_context(plot);
|
|
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_Core_Context_Access::render_context(plot));
|
|
return shower && shower->enabled();
|
|
}
|
|
void set_performance_overlay_enabled(Plot_Core& plot, bool enabled) {
|
|
if (!enabled) {
|
|
auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot));
|
|
if (shower)
|
|
shower->set_enabled(false);
|
|
return;
|
|
}
|
|
auto shower = attach_performance_overlay(plot);
|
|
if (shower) {
|
|
shower->set_enabled(true);
|
|
auto ctx = Plot_Core_Context_Access::render_context(plot);
|
|
shower->set_viewport(plot.viewport_size(), ctx ? ctx->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_Core_Context_Access::render_context(plot));
|
|
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_Core_Context_Access::render_context(plot);
|
|
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 set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function<void(std::string)> callback) {
|
|
auto ctx = Plot_Core_Context_Access::render_context(plot);
|
|
if (!ctx)
|
|
return;
|
|
{
|
|
std::lock_guard<std::mutex> lock(ctx->performance_clipboard_callback_mutex);
|
|
ctx->performance_clipboard_callback = std::move(callback);
|
|
}
|
|
auto shower = performance_overlay_owner(ctx);
|
|
if (shower)
|
|
shower->set_clipboard_callback(performance_clipboard_callback(ctx));
|
|
}
|
|
void notify_performance_overlay_viewport_changed(Plot_Core& plot) {
|
|
auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot));
|
|
if (!shower || !shower->enabled())
|
|
return;
|
|
auto ctx = Plot_Core_Context_Access::render_context(plot);
|
|
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_Core_Context_Access::render_context(plot));
|
|
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_Core_Context_Access::render_context(plot));
|
|
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_Core_Context_Access::render_context(plot));
|
|
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_Core_Context_Access::render_context(plot));
|
|
return shower && shower->enabled() && shower->handle_pointer_release(position);
|
|
}
|
|
bool performance_overlay_handle_pointer_leave(Plot_Core& plot) {
|
|
auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot));
|
|
return shower && shower->enabled() && shower->handle_pointer_leave();
|
|
}
|
|
void Performance_Overlay::set_enabled(bool enabled) {
|
|
auto worker = d()->metrics_worker;
|
|
d()->reset_button_states();
|
|
d()->enabled_value.store(enabled, std::memory_order_release);
|
|
worker->enabled.store(enabled, std::memory_order_release);
|
|
if (!enabled) {
|
|
worker->repaint_requested.store(false, std::memory_order_release);
|
|
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, true);
|
|
}
|
|
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;
|
|
if (options.log.enabled)
|
|
performance_log_service().configure(options.log);
|
|
{
|
|
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, true);
|
|
}
|
|
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_log_options(Performance_Log_Options options) {
|
|
Performance_Overlay_Options value = this->options();
|
|
value.log = std::move(options);
|
|
set_options(std::move(value));
|
|
}
|
|
Performance_Log_Options Performance_Overlay::log_options() const {
|
|
return options().log;
|
|
}
|
|
bool Performance_Overlay::logging_enabled() const {
|
|
return log_options().enabled;
|
|
}
|
|
bool Performance_Overlay::capture_enabled() const {
|
|
return enabled() || logging_enabled();
|
|
}
|
|
void Performance_Overlay::set_log_identity(Performance_Log_Plot_Identity identity) {
|
|
d()->set_identity(std::move(identity));
|
|
}
|
|
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;
|
|
value.section_color = color;
|
|
value.label_color = color;
|
|
value.value_color = color;
|
|
value.unit_color = 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 (!frame)
|
|
return;
|
|
if (enabled())
|
|
d()->enqueue_frame_metric(frame);
|
|
if (logging_enabled())
|
|
d()->enqueue_frame_log(frame);
|
|
}
|
|
void Performance_Overlay::collect_feedback_event(std::shared_ptr<const Frame_Feedback_Event_Record> event) {
|
|
if (!event || !logging_enabled())
|
|
return;
|
|
d()->enqueue_feedback_log(event);
|
|
}
|
|
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, true);
|
|
}
|
|
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, true);
|
|
}
|
|
return true;
|
|
}
|
|
bool Performance_Overlay::handle_pointer_press(PointF position) {
|
|
auto snapshot = display_snapshot();
|
|
if (!snapshot)
|
|
return false;
|
|
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_press(position, snapshot->copy_button_rect);
|
|
Button_Event_Result close_result = d()->close_button_interaction.pointer_press(position, snapshot->close_button_rect);
|
|
d()->publish_button_states(copy_result.changed || close_result.changed);
|
|
if (copy_result.consumed || close_result.consumed)
|
|
return true;
|
|
if (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, true);
|
|
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, true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool Performance_Overlay::handle_pointer_move(PointF position) {
|
|
if (d()->metrics_worker->drag_active.load(std::memory_order_acquire)) {
|
|
d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release);
|
|
schedule_performance_metrics_worker(d()->metrics_worker, true);
|
|
return true;
|
|
}
|
|
auto snapshot = display_snapshot();
|
|
if (!snapshot)
|
|
return false;
|
|
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_move(position, snapshot->copy_button_rect);
|
|
Button_Event_Result close_result = d()->close_button_interaction.pointer_move(position, snapshot->close_button_rect);
|
|
d()->publish_button_states(copy_result.changed || close_result.changed);
|
|
return copy_result.consumed || close_result.consumed;
|
|
}
|
|
bool Performance_Overlay::handle_pointer_release(PointF position) {
|
|
auto snapshot = display_snapshot();
|
|
if (snapshot) {
|
|
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_release(position, snapshot->copy_button_rect);
|
|
Button_Event_Result close_result = d()->close_button_interaction.pointer_release(position, snapshot->close_button_rect);
|
|
d()->publish_button_states(copy_result.changed || close_result.changed);
|
|
if (copy_result.clicked)
|
|
d()->copy_to_clipboard(performance_snapshot_text(*snapshot));
|
|
if (close_result.clicked)
|
|
set_enabled(false);
|
|
if (copy_result.consumed || close_result.consumed)
|
|
return true;
|
|
}
|
|
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, true);
|
|
return true;
|
|
}
|
|
bool Performance_Overlay::handle_pointer_leave() {
|
|
Button_Event_Result copy_result = d()->copy_button_interaction.pointer_leave();
|
|
Button_Event_Result close_result = d()->close_button_interaction.pointer_leave();
|
|
d()->publish_button_states(copy_result.changed || close_result.changed);
|
|
return copy_result.consumed || close_result.consumed;
|
|
}
|
|
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 Performance_Overlay::set_clipboard_callback(std::function<void(std::string)> callback) {
|
|
d()->set_clipboard_callback(std::move(callback));
|
|
}
|
|
void write_frame_performance_log(std::shared_ptr<const Frame_Lifecycle_Record> frame) {
|
|
if (!frame)
|
|
return;
|
|
performance_log_service().enqueue_frame({}, std::move(frame));
|
|
}
|
|
} // namespace renderive
|