Profiler: the Profile operations preference switches recording at run time

- Graphics page: "Profile operations" (off by default) starts and stops the
  profiler when it changes; an agent's diagnostics.profile in between stands,
  and SOLIDCRAFT_PROFILE=1 is not undone by the default at start-up.
- The desktop app reads SOLIDCRAFT_PROFILE at start-up.
- Labels in en, ar, de, he, ja, ko, zh. The pending-messages test compares
  right-to-left pending text in display order, as the catalogs give it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-27 08:22:36 -04:00
committed by Irakli Kourtis
parent 9cd655259d
commit fafd977b05
5 changed files with 74 additions and 1 deletions
+2
View File
@@ -679,6 +679,8 @@ fn main() -> eframe::Result {
#[cfg(target_os = "macos")]
objc2_foundation::NSProcessInfo::processInfo().setProcessName(objc2_foundation::ns_string!("SolidCraft"));
mark("main");
// SOLIDCRAFT_PROFILE=1: the profiler records from the start (diagnostics.profile reads it).
solidcraft_engine::timing::profile_from_env();
// Validate before legacy adoption, crash markers or any user preference read.
let settings = settings_dir().map_err(|error| eframe::Error::AppCreation(error.into()))?;
if std::env::var_os("SOLIDCRAFT_SETTINGS_DIR").is_none() {
+37
View File
@@ -0,0 +1,37 @@
{
"en": {
"cmd.diagnostics_profile": "Profile",
"prefs.profile_operations": "Profile operations",
"prefs.profile_operations_note": "Records how long SMLib calls, kernel operations, features, commands and imports take. Agents read it with diagnostics.profile."
},
"ar": {
"cmd.diagnostics_profile": "التحليل الزمني",
"prefs.profile_operations": "تحليل زمن العمليات",
"prefs.profile_operations_note": "يسجّل المدة التي تستغرقها استدعاءات SMLib وعمليات النواة والميزات والأوامر وعمليات الاستيراد. يقرؤه الوكلاء عبر diagnostics.profile."
},
"de": {
"cmd.diagnostics_profile": "Profil",
"prefs.profile_operations": "Vorgänge profilieren",
"prefs.profile_operations_note": "Zeichnet auf, wie lange SMLib-Aufrufe, Kernel-Vorgänge, Features, Befehle und Importe dauern. Agenten lesen es mit diagnostics.profile."
},
"he": {
"cmd.diagnostics_profile": "פרופיל ביצועים",
"prefs.profile_operations": "מדידת זמני פעולות",
"prefs.profile_operations_note": "מתעד כמה זמן לוקחות קריאות SMLib, פעולות ליבה, תכונות, פקודות וייבוא. סוכנים קוראים זאת עם diagnostics.profile."
},
"ja": {
"cmd.diagnostics_profile": "プロファイル",
"prefs.profile_operations": "処理をプロファイル",
"prefs.profile_operations_note": "SMLib の呼び出し、カーネル処理、フィーチャー、コマンド、インポートにかかる時間を記録します。エージェントは diagnostics.profile で読み取ります。"
},
"ko": {
"cmd.diagnostics_profile": "프로파일",
"prefs.profile_operations": "작업 프로파일링",
"prefs.profile_operations_note": "SMLib 호출, 커널 작업, 피처, 명령, 가져오기에 걸리는 시간을 기록합니다. 에이전트는 diagnostics.profile로 읽습니다."
},
"zh": {
"cmd.diagnostics_profile": "性能分析",
"prefs.profile_operations": "分析操作耗时",
"prefs.profile_operations_note": "记录 SMLib 调用、内核操作、特征、命令和导入所用的时间。智能体通过 diagnostics.profile 读取。"
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ nix = { workspace = true }
winreg = "0.56"
[dev-dependencies]
solidcraft-engine = { workspace = true, features = ["bundled-render-library"] }
solidcraft-engine = { workspace = true, features = ["bundled-render-library", "profile"] }
# Parse the source guard structurally, including code after inline test modules.
syn = { workspace = true, features = ["visit"] }
# The catalog guard reads every UI source file as Rust tokens; already in the tree through
+33
View File
@@ -602,6 +602,9 @@ preference_settings! {
supersampling_adaptive: bool = true, PAGE_GRAPHICS => check_tr("ui.supersampling_adaptive");
/// Show the frame rate in the view's corner (the view then redraws every frame).
show_frame_rate: bool = false, PAGE_GRAPHICS => check_id("ui.frame_rate");
/// Record where the time goes (SMLib's calls, kernel operations, features, commands, imports)
/// for `diagnostics.profile` (docs/perf.md). Off by default: nothing is recorded.
profile_operations: bool = false, PAGE_GRAPHICS => check_tr("prefs.profile_operations");
/// With the frame rate: a frame-time graph, a histogram and what the GPU drew (#2548).
frame_rate_details: bool = false, PAGE_GRAPHICS;
/// Draw a wider rim under finished sketch curves to improve contrast on shaded faces.
@@ -830,6 +833,9 @@ pub struct PrefsWindow {
snapshot: Option<Snapshot>,
/// The up axis preference the view follows now (applied when it changes, and at startup).
applied_up: Option<String>,
/// The Profile operations preference the profiler last followed: applied when it changes,
/// so `diagnostics.profile` (an agent) and SOLIDCRAFT_PROFILE can switch it in between.
applied_profile: Option<bool>,
/// The search text of the interface font list.
font_filter: String,
/// Search text on the Plugins page.
@@ -1096,6 +1102,15 @@ fn defaults(app: &mut SolveApp, page: usize) {
/// the interface scale.
pub fn apply(app: &mut SolveApp, ctx: &egui::Context) {
apply_network(&app.preferences);
// The profiler records while the preference is on (switched when it changes; at start-up only
// when on, so SOLIDCRAFT_PROFILE=1 is not undone by the default).
let profile = app.preferences.profile_operations;
if app.prefs_window.applied_profile != Some(profile) {
if profile || app.prefs_window.applied_profile.is_some() {
solidcraft_engine::profile::set_enabled(profile);
}
app.prefs_window.applied_profile = Some(profile);
}
// The viewport's display quality (an unknown saved value draws at Normal).
solidcraft_engine::doc::display_quality::set_current(
solidcraft_engine::doc::display_quality::DisplayQuality::parse(&app.preferences.display_quality).unwrap_or_default(),
@@ -2389,6 +2404,24 @@ pub(crate) fn reveal_preference(h: &mut crate::scenario::Harness, handle: &str)
#[cfg(test)]
mod tests {
/// Profile operations switches the profiler when it changes, and only then: an agent's
/// diagnostics.profile in between is not undone next frame.
#[test]
fn the_profile_preference_switches_the_profiler_when_it_changes() {
use serde_json::json;
let mut h = crate::scenario::Harness::new();
h.frames(1);
h.call("ui.prefs", json!({"profile_operations": true}));
h.frames(2);
assert!(solidcraft_engine::profile::enabled());
h.app.session.execute("diagnostics.profile", &json!({"enabled": false})).unwrap();
h.frames(2);
assert!(!solidcraft_engine::profile::enabled(), "the command's choice stands");
h.call("ui.prefs", json!({"profile_operations": false}));
h.frames(2);
assert!(!solidcraft_engine::profile::enabled());
}
#[test]
fn the_ghost_position_defaults_round_trips_and_reads_tolerantly() {
use super::SpaceMouseGhost::{LeftViewport as Current, LeftWindow as Left, RightViewport as Right};
+1
View File
@@ -163,6 +163,7 @@ pub(super) fn show(app: &mut SolveApp, ui: &mut egui::Ui) {
ui_kit::checkbox(ui, app.tr("ui.frame_rate_details"), &mut app.preferences.frame_rate_details)
.on_hover_text(app.tr("settings.frame_rate_details_note"));
});
setting::profile_operations(app, left).on_hover_text(app.tr("prefs.profile_operations_note"));
setting::reference_geometry_through_bodies(app, left).on_hover_text(app.tr("settings.reference_geometry_through_bodies_help"));
setting::sketch_halo(app, left);
}