Private
Public Access
0
0

improved startup first frame boot

This commit is contained in:
2026-06-07 01:08:31 -04:00
parent af274df837
commit 4b34f83970
8 changed files with 232 additions and 88 deletions
+146 -48
View File
@@ -161,6 +161,75 @@ def truncate_entries(entries: list[dict[str, Any]], max_pairs: int) -> list[dict
if count == target: return entries[i:]
return entries
def _detect_refresh_rate_win32() -> float:
"""Return the primary display's current refresh rate in Hz, or 0.0 on failure.
Uses user32.EnumDisplaySettingsW (ENUM_CURRENT_SETTINGS) which reads the value
directly from the display driver in microseconds. The previous implementation
shelled out to PowerShell + WMI (Get-CimInstance Win32_VideoController), which
cost ~350ms on every startup and blocked the first frame.
"""
try:
import ctypes
from ctypes import wintypes
class _DEVMODE(ctypes.Structure):
_fields_ = [
("dmDeviceName", wintypes.WCHAR * 32), ("dmSpecVersion", wintypes.WORD),
("dmDriverVersion", wintypes.WORD), ("dmSize", wintypes.WORD),
("dmDriverExtra", wintypes.WORD), ("dmFields", wintypes.DWORD),
("dmStuff", ctypes.c_byte * 16), ("dmColor", wintypes.SHORT),
("dmDuplex", wintypes.SHORT), ("dmYResolution", wintypes.SHORT),
("dmTTOption", wintypes.SHORT), ("dmCollate", wintypes.SHORT),
("dmFormName", wintypes.WCHAR * 32), ("dmLogPixels", wintypes.WORD),
("dmBitsPerPel", wintypes.DWORD), ("dmPelsWidth", wintypes.DWORD),
("dmPelsHeight", wintypes.DWORD), ("dmDisplayFlags", wintypes.DWORD),
("dmDisplayFrequency", wintypes.DWORD), ("dmICMMethod", wintypes.DWORD),
("dmICMIntent", wintypes.DWORD), ("dmMediaType", wintypes.DWORD),
("dmDitherType", wintypes.DWORD), ("dmReserved1", wintypes.DWORD),
("dmReserved2", wintypes.DWORD), ("dmPanningWidth", wintypes.DWORD),
("dmPanningHeight", wintypes.DWORD),
]
dm = _DEVMODE()
dm.dmSize = ctypes.sizeof(_DEVMODE)
if ctypes.windll.user32.EnumDisplaySettingsW(None, -1, ctypes.byref(dm)):
# dmDisplayFrequency is 0 or 1 for "default/hardware" on some drivers.
if dm.dmDisplayFrequency > 1:
return float(dm.dmDisplayFrequency)
except Exception:
pass
return 0.0
def _resolve_font_path(font_path: str, assets_dir: Path) -> str:
"""Normalize a configured font path to something hello_imgui can load.
hello_imgui resolves relative paths against the assets folder. A config may
carry a stale ABSOLUTE path from a different project checkout (e.g.
C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf after the repo
moved to C:/projects/sloppy). In that case the absolute file does not exist
and the load fails. This recovers by:
1. If the absolute path lives under the current assets folder -> relativize.
2. If the absolute path exists on disk as-is -> keep it.
3. Otherwise recover the basename under assets/fonts or assets.
4. Final fallback: the bundled default Inter font.
"""
p = Path(font_path)
if not p.is_absolute():
return font_path # already relative; hello_imgui searches the assets folder
try:
if p.is_relative_to(assets_dir):
return str(p.relative_to(assets_dir)).replace("\\", "/")
except (ValueError, AttributeError):
pass
if p.exists():
return font_path # absolute path to a real file elsewhere — load directly
# Stale absolute path: recover by basename relative to the assets folder.
name = p.name
for rel in (f"fonts/{name}", name):
if (assets_dir / rel).exists():
return rel
return "fonts/Inter-Regular.ttf"
class App:
"""The main ImGui interface orchestrator for Manual Slop."""
@@ -172,7 +241,7 @@ class App:
# --- Core Dependencies & State ---
from src.startup_profiler import startup_profiler
with startup_profiler.phase("app_init_AppController"):
self.controller = app_controller.AppController()
self.controller = app_controller.AppController(defer_warmup=True)
self.controller._app = self
with startup_profiler.phase("app_init_history_perfmon"):
from src import history, performance_monitor
@@ -187,7 +256,8 @@ class App:
# --- Command Palette ---
self.show_command_palette: bool = False
# --- Initialization ---
self.controller.init_state()
with startup_profiler.phase("app_init_state"):
self.controller.init_state()
from src.hot_reloader import HotReloader, HotModule
if 'src.gui_2' not in HotReloader.HOT_MODULES:
HotReloader.register(HotModule(
@@ -196,11 +266,13 @@ class App:
state_keys=['active_discussion', 'show_windows', 'ui_file_paths', 'ui_screenshot_paths', 'disc_entries', 'disc_roles'],
delegation_targets=['_render_main_interface', '_render_discussion_hub', '_render_files_and_media', '_render_ai_settings_hub', '_render_operations_hub', '_render_mma_dashboard']
))
self.workspace_manager = workspace_manager.WorkspaceManager(project_root=self.controller.active_project_root)
self.disc_entries = self.controller.disc_entries
self.disc_roles = self.controller.disc_roles
self.workspace_profiles = self.workspace_manager.load_all_profiles()
self.controller.start_services(self)
with startup_profiler.phase("app_init_workspace"):
self.workspace_manager = workspace_manager.WorkspaceManager(project_root=self.controller.active_project_root)
self.disc_entries = self.controller.disc_entries
self.disc_roles = self.controller.disc_roles
self.workspace_profiles = self.workspace_manager.load_all_profiles()
with startup_profiler.phase("app_init_start_services"):
self.controller.start_services(self)
# --- Controller Callbacks & Actions ---
self.controller._predefined_callbacks['save_context_preset'] = self.save_context_preset
self.controller._predefined_callbacks['load_context_preset'] = self.load_context_preset
@@ -457,16 +529,13 @@ class App:
user_scale = theme.get_current_scale()
self.runner_params.dpi_aware_params.dpi_window_size_factor = user_scale
# Detect Monitor Refresh Rate for capping (Win32 only)
# Detect Monitor Refresh Rate for capping (Win32 only).
# Uses the native EnumDisplaySettings call (~0.3ms) instead of spawning a
# PowerShell/WMI subprocess (~350ms) so the first frame is not blocked.
fps_cap = 60.0
if sys.platform == "win32":
try:
# Use PowerShell to get max refresh rate across all controllers
cmd = "powershell -NoProfile -Command \"Get-CimInstance -ClassName Win32_VideoController | Select-Object -ExpandProperty CurrentRefreshRate\""
out = subprocess.check_output(cmd, shell=True).decode().splitlines()
rates = [float(r.strip()) for r in out if r.strip().isdigit()]
if rates: fps_cap = max(rates)
except Exception: pass
rate = _detect_refresh_rate_win32()
if rate: fps_cap = rate
# Enable idling with monitor refresh rate to effectively cap FPS
self.runner_params.fps_idling.enable_idling = True
@@ -476,11 +545,17 @@ class App:
self.runner_params.imgui_window_params.show_menu_view_themes = True
self.runner_params.ini_folder_type = hello_imgui.IniFolderType.current_folder
self.runner_params.ini_filename = "manualslop_layout.ini"
def _profiled_setup_style() -> None:
with startup_profiler.phase("setup_imgui_style"):
theme.apply_current()
def _profiled_post_init() -> None:
with startup_profiler.phase("post_init"):
self._post_init()
self.runner_params.callbacks.show_gui = self._gui_func
self.runner_params.callbacks.show_menus = self._show_menus
self.runner_params.callbacks.load_additional_fonts = self._load_fonts
self.runner_params.callbacks.setup_imgui_style = theme.apply_current
self.runner_params.callbacks.post_init = self._post_init
self.runner_params.callbacks.setup_imgui_style = _profiled_setup_style
self.runner_params.callbacks.post_init = _profiled_post_init
self._fetch_models(self.current_provider)
md_options = markdown_helper.get_renderer().options
immapp.run(self.runner_params, add_ons_params=immapp.AddOnsParams(with_markdown_options=md_options))
@@ -489,42 +564,39 @@ class App:
session_logger.close_session()
def _load_fonts(self) -> None:
# Set hello_imgui assets folder to the actual absolute path
assets_dir = Path(__file__).parent.parent / "assets"
if assets_dir.exists():
hello_imgui.set_assets_folder(str(assets_dir.absolute()))
from src.startup_profiler import startup_profiler
with startup_profiler.phase("load_fonts"):
# Set hello_imgui assets folder to the actual absolute path
assets_dir = Path(__file__).parent.parent / "assets"
if assets_dir.exists():
hello_imgui.set_assets_folder(str(assets_dir.absolute()))
# Improved font rendering with oversampling
config = imgui.ImFontConfig()
config.oversample_h = 3
config.oversample_v = 3
# Improved font rendering with oversampling
config = imgui.ImFontConfig()
config.oversample_h = 3
config.oversample_v = 3
font_path, font_size = theme.get_font_loading_params()
if font_path:
p = Path(font_path)
if p.is_absolute():
font_path, font_size = theme.get_font_loading_params()
if font_path:
font_path = _resolve_font_path(font_path, assets_dir)
# Just try loading it directly; hello_imgui will look in the assets folder
try:
if p.is_relative_to(assets_dir):
font_path = str(p.relative_to(assets_dir)).replace("\\", "/")
except (ValueError, AttributeError):
pass # Fallback to original font_path if relative_to fails or on old Python
# Just try loading it directly; hello_imgui will look in the assets folder
try:
self.main_font = hello_imgui.load_font_ttf_with_font_awesome_icons(font_path, font_size, config)
except Exception as e:
print(f"Failed to load main font {font_path}: {e}")
with startup_profiler.phase("load_fonts.main_with_fontawesome"):
self.main_font = hello_imgui.load_font_ttf_with_font_awesome_icons(font_path, font_size, config)
except Exception as e:
print(f"Failed to load main font {font_path}: {e}")
self.main_font = None
else:
self.main_font = None
else:
self.main_font = None
try:
params = hello_imgui.FontLoadingParams(font_config=config)
self.mono_font = hello_imgui.load_font("fonts/MapleMono-Regular.ttf", font_size, params)
except Exception as e:
print(f"Failed to load mono font: {e}")
self.mono_font = None
try:
with startup_profiler.phase("load_fonts.mono"):
params = hello_imgui.FontLoadingParams(font_config=config)
self.mono_font = hello_imgui.load_font("fonts/MapleMono-Regular.ttf", font_size, params)
except Exception as e:
print(f"Failed to load mono font: {e}")
self.mono_font = None
def _handle_approve_mma_step(self, user_data=None) -> None:
"""UI-level wrapper for approving a pending MMA step."""
@@ -817,6 +889,32 @@ class App:
# ---------------------------------------------------------------- gui
def _gui_func(self) -> None:
# One-shot: log when immapp first hands control to our render callback. The
# span init -> here is window/GL/context creation + the font/style/post_init
# callbacks (all opaque C++); the span here -> mark_first_frame_rendered is
# the cost of the first full-UI render. Splitting them attributes the gap.
if not getattr(self, "_gui_func_entered", False):
self._gui_func_entered = True
try:
init_ts = getattr(self.controller, "_init_start_ts", None)
if init_ts is not None:
sys.stderr.write(f"[startup] first _gui_func entry at {(time.time() - init_ts) * 1000:.1f}ms after init (window/GL + font/style/post_init callbacks done)\n")
sys.stderr.flush()
except Exception: pass
# One-shot: kick off the controller's heavy-module warmup on the shared
# io_pool once the FIRST frame has actually been painted. Waiting one frame
# keeps the ~2s of SDK C-extension imports from holding the GIL during
# window creation and font-atlas building, so the window appears at full
# speed; the SDKs are then warmed by the time the user sends their first
# message. start_warmup() is idempotent.
if not getattr(self, "_preload_started", False):
if getattr(self, "_first_frame_painted", False):
self.controller.start_warmup()
self._preload_started = True
else:
self._first_frame_painted = True
io = imgui.get_io()
if io.key_ctrl and io.key_alt and imgui.is_key_down(imgui.Key.r):
self._trigger_hot_reload()