mirror of https://codeberg.org/topola/topola.git
Add profiling capabilities (including GUI debug profiler)
This commit is contained in:
parent
8df8d760c3
commit
a7fcf57a75
|
|
@ -51,6 +51,7 @@ tr-menu-debug-show-bboxes = Show BBoxes
|
||||||
tr-menu-debug-show-primitive-indices = Show Primitive Indices
|
tr-menu-debug-show-primitive-indices = Show Primitive Indices
|
||||||
tr-menu-debug-show-bend-endpoint-tangents = Show Bend Endpoint Tangents
|
tr-menu-debug-show-bend-endpoint-tangents = Show Bend Endpoint Tangents
|
||||||
tr-menu-debug-fix-step-rate = Fix Step Rate
|
tr-menu-debug-fix-step-rate = Fix Step Rate
|
||||||
|
tr-menu-debug-profiler = Profiler
|
||||||
tr-menu-debug-step-rate = Step Rate
|
tr-menu-debug-step-rate = Step Rate
|
||||||
tr-menu-debug-step-rate-unit = steps/s
|
tr-menu-debug-step-rate-unit = steps/s
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ edition = "2024"
|
||||||
[features]
|
[features]
|
||||||
default = ["xdg-portal"]
|
default = ["xdg-portal"]
|
||||||
gtk3 = ["rfd/gtk3"]
|
gtk3 = ["rfd/gtk3"]
|
||||||
|
profiler = ["puffin", "puffin_egui", "puffin_http", "topola/profiler"]
|
||||||
xdg-portal = ["rfd/xdg-portal"]
|
xdg-portal = ["rfd/xdg-portal"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
|
@ -41,6 +42,9 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
futures-lite = "2.6"
|
futures-lite = "2.6"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
|
puffin = { version = "0.20", optional = true }
|
||||||
|
puffin_egui = { version = "0.30", optional = true }
|
||||||
|
puffin_http = { version = "0.17", optional = true }
|
||||||
|
|
||||||
# Web:
|
# Web:
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ impl eframe::App for App {
|
||||||
|
|
||||||
/// Called each time the UI has to be repainted.
|
/// Called each time the UI has to be repainted.
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
crate::profiler::begin_frame();
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
self.menu_bar.update(
|
self.menu_bar.update(
|
||||||
ctx,
|
ctx,
|
||||||
&mut self.translator,
|
&mut self.translator,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ impl Controller {
|
||||||
step_rate: Option<f64>,
|
step_rate: Option<f64>,
|
||||||
dt: f64,
|
dt: f64,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
crate::profile_function!();
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
|
|
||||||
if step_rate.is_some() {
|
if step_rate.is_some() {
|
||||||
|
|
@ -61,17 +62,18 @@ impl Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, _tr: &Translator) -> ControlFlow<()> {
|
pub fn step(&mut self, _tr: &Translator) -> ControlFlow<()> {
|
||||||
|
crate::profile_function!();
|
||||||
self.master_interactor.step(self.workspace.board_mut())
|
self.master_interactor.step(self.workspace.board_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
||||||
|
crate::profile_function!();
|
||||||
let Self {
|
let Self {
|
||||||
workspace,
|
workspace,
|
||||||
appearance_panel,
|
appearance_panel,
|
||||||
master_interactor: _,
|
master_interactor: _,
|
||||||
dt_accum,
|
dt_accum,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
appearance_panel.update(ctx, workspace.board_mut());
|
appearance_panel.update(ctx, workspace.board_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,6 +86,7 @@ impl Controller {
|
||||||
scene_hovered: bool,
|
scene_hovered: bool,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||||
let board_master =
|
let board_master =
|
||||||
if let MasterInteractor::Autoplacer(interactor) = &self.master_interactor {
|
if let MasterInteractor::Autoplacer(interactor) = &self.master_interactor {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ impl Display {
|
||||||
viewport: &Viewport,
|
viewport: &Viewport,
|
||||||
workspace: &Controller,
|
workspace: &Controller,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
self.display_layout(ctx, ui, /*menu_bar,*/ viewport, workspace);
|
self.display_layout(ctx, ui, /*menu_bar,*/ viewport, workspace);
|
||||||
self.display_repulsions(ui, viewport, workspace);
|
self.display_repulsions(ui, viewport, workspace);
|
||||||
self.display_attractions(ui, viewport, workspace);
|
self.display_attractions(ui, viewport, workspace);
|
||||||
|
|
@ -40,6 +42,7 @@ impl Display {
|
||||||
viewport: &Viewport,
|
viewport: &Viewport,
|
||||||
workspace: &Controller,
|
workspace: &Controller,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
let board = workspace.workspace.board();
|
let board = workspace.workspace.board();
|
||||||
let layout = board.layout();
|
let layout = board.layout();
|
||||||
|
|
||||||
|
|
@ -162,6 +165,7 @@ impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_repulsions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
fn display_repulsions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
||||||
|
crate::profile_function!();
|
||||||
let board = workspace.workspace.board();
|
let board = workspace.workspace.board();
|
||||||
let stroke = egui::Stroke::new(150.0 / viewport.scale_factor(), egui::Color32::YELLOW);
|
let stroke = egui::Stroke::new(150.0 / viewport.scale_factor(), egui::Color32::YELLOW);
|
||||||
|
|
||||||
|
|
@ -203,6 +207,7 @@ impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_retentions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
fn display_retentions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
||||||
|
crate::profile_function!();
|
||||||
let board = workspace.workspace.board();
|
let board = workspace.workspace.board();
|
||||||
let layout = board.layout();
|
let layout = board.layout();
|
||||||
let stroke = egui::Stroke::new(
|
let stroke = egui::Stroke::new(
|
||||||
|
|
@ -244,6 +249,7 @@ impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_attractions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
fn display_attractions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
||||||
|
crate::profile_function!();
|
||||||
let board = workspace.workspace.board();
|
let board = workspace.workspace.board();
|
||||||
let layout = board.layout();
|
let layout = board.layout();
|
||||||
let stroke = egui::Stroke::new(150.0 / viewport.scale_factor(), egui::Color32::BLUE);
|
let stroke = egui::Stroke::new(150.0 / viewport.scale_factor(), egui::Color32::BLUE);
|
||||||
|
|
@ -375,6 +381,7 @@ impl Display {
|
||||||
viewport: &Viewport,
|
viewport: &Viewport,
|
||||||
workspace: &Controller,
|
workspace: &Controller,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
let board = workspace.workspace.board();
|
let board = workspace.workspace.board();
|
||||||
let layout = board.layout();
|
let layout = board.layout();
|
||||||
|
|
||||||
|
|
@ -452,6 +459,7 @@ impl Display {
|
||||||
viewport: &Viewport,
|
viewport: &Viewport,
|
||||||
workspace: &Controller,
|
workspace: &Controller,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
let Workspace::Autorouter(autorouter_workspace) = &workspace.workspace else {
|
let Workspace::Autorouter(autorouter_workspace) = &workspace.workspace else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -522,6 +530,7 @@ impl Display {
|
||||||
_viewport: &Viewport,
|
_viewport: &Viewport,
|
||||||
workspace: &Controller,
|
workspace: &Controller,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
let Workspace::Autorouter(autorouter_workspace) = &workspace.workspace else {
|
let Workspace::Autorouter(autorouter_workspace) = &workspace.workspace else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ mod controller;
|
||||||
mod display;
|
mod display;
|
||||||
mod layers_panel;
|
mod layers_panel;
|
||||||
mod menu_bar;
|
mod menu_bar;
|
||||||
|
mod profiler;
|
||||||
mod translator;
|
mod translator;
|
||||||
mod viewport;
|
mod viewport;
|
||||||
|
|
||||||
|
|
@ -21,6 +22,8 @@ use crate::app::App;
|
||||||
fn main() -> eframe::Result {
|
fn main() -> eframe::Result {
|
||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
|
|
||||||
|
profiler::enable();
|
||||||
|
|
||||||
let native_options = eframe::NativeOptions {
|
let native_options = eframe::NativeOptions {
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
.with_inner_size([400.0, 300.0])
|
.with_inner_size([400.0, 300.0])
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ use crate::{
|
||||||
pub struct MenuBar {
|
pub struct MenuBar {
|
||||||
pub fix_step_rate: bool,
|
pub fix_step_rate: bool,
|
||||||
pub step_rate: f64,
|
pub step_rate: f64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub show_profiler: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MenuBar {
|
impl MenuBar {
|
||||||
|
|
@ -34,6 +36,7 @@ impl MenuBar {
|
||||||
Self {
|
Self {
|
||||||
fix_step_rate: false,
|
fix_step_rate: false,
|
||||||
step_rate: 1.0,
|
step_rate: 1.0,
|
||||||
|
show_profiler: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,6 +47,8 @@ impl MenuBar {
|
||||||
content_sender: Sender<Result<DsnFile, ParseErrorContext>>,
|
content_sender: Sender<Result<DsnFile, ParseErrorContext>>,
|
||||||
controller: Option<&mut Controller>,
|
controller: Option<&mut Controller>,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
let mut actions = Actions::new(tr);
|
let mut actions = Actions::new(tr);
|
||||||
|
|
||||||
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
||||||
|
|
@ -76,6 +81,15 @@ impl MenuBar {
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
{
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
if ui.button(tr.text("tr-menu-debug-profiler")).clicked() {
|
||||||
|
self.show_profiler = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
@ -118,5 +132,7 @@ impl MenuBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
crate::profiler::profiler_window(ctx, &mut self.show_profiler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
static PUFFIN_SERVER: std::sync::OnceLock<puffin_http::Server> = std::sync::OnceLock::new();
|
||||||
|
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
pub fn enable() {
|
||||||
|
let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT);
|
||||||
|
PUFFIN_SERVER.get_or_init(|| {
|
||||||
|
eprintln!("Run this to view profiler data: puffin_viewer {server_addr}");
|
||||||
|
puffin_http::Server::new(&server_addr).expect("puffin_http server")
|
||||||
|
});
|
||||||
|
puffin::set_scopes_on(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "wasm32", not(feature = "profiler")))]
|
||||||
|
pub fn enable() {}
|
||||||
|
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
pub fn begin_frame() {
|
||||||
|
puffin::GlobalProfiler::lock().new_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "wasm32", not(feature = "profiler")))]
|
||||||
|
pub fn begin_frame() {}
|
||||||
|
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
pub fn profiler_window(ctx: &egui::Context, open: &mut bool) {
|
||||||
|
if *open {
|
||||||
|
*open = puffin_egui::profiler_window(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "wasm32", not(feature = "profiler")))]
|
||||||
|
pub fn profiler_window(_ctx: &egui::Context, _open: &mut bool) {}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! profile_scope {
|
||||||
|
($name:expr) => {
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
puffin::profile_scope!($name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! profile_function {
|
||||||
|
() => {
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
puffin::profile_function!();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,8 @@ impl Viewport {
|
||||||
menu_bar: &MenuBar,
|
menu_bar: &MenuBar,
|
||||||
controller: Option<&mut Controller>,
|
controller: Option<&mut Controller>,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||||
ui.ctx().request_repaint();
|
ui.ctx().request_repaint();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ description = "Work-in-progress free and open-source topological (rubberband) ro
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
profiler = ["puffin"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bidimap = "0.7"
|
bidimap = "0.7"
|
||||||
dearcut = { version = "0.3", features = ["serde", "undoredo"] }
|
dearcut = { version = "0.3", features = ["serde", "undoredo"] }
|
||||||
|
|
@ -24,6 +27,7 @@ spade = "2.15"
|
||||||
specctra = { path = "../specctra" }
|
specctra = { path = "../specctra" }
|
||||||
stable-vec = "0.4"
|
stable-vec = "0.4"
|
||||||
undoredo.workspace = true
|
undoredo.workspace = true
|
||||||
|
puffin = { version = "0.20", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
walkdir = "2.5"
|
walkdir = "2.5"
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ impl AutoplacerMasterInteractor {
|
||||||
|
|
||||||
impl Interactor for AutoplacerMasterInteractor {
|
impl Interactor for AutoplacerMasterInteractor {
|
||||||
fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
||||||
|
crate::profile_function!();
|
||||||
self.autoplacer.step(board)
|
self.autoplacer.step(board)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use undoredo::{FlushDelta, ResetDelta};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
board::{Board, BoardDelta},
|
board::{Board, BoardDelta},
|
||||||
layout::compounds::ComponentId,
|
layout::{Layout, compounds::ComponentId},
|
||||||
orientation::Orientation,
|
orientation::Orientation,
|
||||||
selections::ComponentSelection,
|
selections::ComponentSelection,
|
||||||
vector::Vector2,
|
vector::Vector2,
|
||||||
|
|
@ -54,6 +54,8 @@ impl Autoplacer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
pub fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
if self.step_counter < self.schedule.max_steps {
|
if self.step_counter < self.schedule.max_steps {
|
||||||
let control_flow = self.step_with_params(
|
let control_flow = self.step_with_params(
|
||||||
board,
|
board,
|
||||||
|
|
@ -88,37 +90,59 @@ impl Autoplacer {
|
||||||
board: &mut Board,
|
board: &mut Board,
|
||||||
params: AutoplacerStepParams,
|
params: AutoplacerStepParams,
|
||||||
) -> ControlFlow<()> {
|
) -> ControlFlow<()> {
|
||||||
for &component in self.components.iter() {
|
crate::profile_function!();
|
||||||
//self.step_component_with_params(component, params);
|
|
||||||
//let last_cost = self.cost(board, params);
|
|
||||||
let last_cost = self.component_cost(board, component, params);
|
|
||||||
|
|
||||||
let dx_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
for i in 0..self.components.len() {
|
||||||
let dy_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
let component = self.components[i];
|
||||||
|
self.step_component(board, component, params);
|
||||||
//let dx = dx_gaussian.sample(&mut self.rng);
|
|
||||||
//let dy = dy_gaussian.sample(&mut self.rng);
|
|
||||||
let dx = dx_gaussian.sample(&mut rand::rng());
|
|
||||||
let dy = dy_gaussian.sample(&mut rand::rng());
|
|
||||||
|
|
||||||
board.move_resolved_components_by(&[component], Vector2::new(dx as i64, dy as i64));
|
|
||||||
|
|
||||||
//let new_cost = self.cost(board, params);
|
|
||||||
let new_cost = self.component_cost(board, component, params);
|
|
||||||
let delta_cost = new_cost - last_cost;
|
|
||||||
|
|
||||||
if delta_cost < 0.0
|
|
||||||
|| rand::rng().random::<f64>() < f64::exp(-delta_cost / params.temperature)
|
|
||||||
{
|
|
||||||
self.origin_delta = self.origin_delta.clone().merge_delta(board.flush_delta());
|
|
||||||
} else {
|
|
||||||
board.reset_delta();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn step_component(
|
||||||
|
&mut self,
|
||||||
|
board: &mut Board,
|
||||||
|
component: ComponentId,
|
||||||
|
params: AutoplacerStepParams,
|
||||||
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
|
let last_cost = self.component_cost(board, component, params);
|
||||||
|
let translation = self.sample_move(params);
|
||||||
|
board.move_resolved_components_by(&[component], translation);
|
||||||
|
let new_cost = self.component_cost(board, component, params);
|
||||||
|
let delta_cost = new_cost - last_cost;
|
||||||
|
|
||||||
|
if delta_cost < 0.0
|
||||||
|
|| rand::rng().random::<f64>() < f64::exp(-delta_cost / params.temperature)
|
||||||
|
{
|
||||||
|
self.accept_move(board);
|
||||||
|
} else {
|
||||||
|
self.reject_move(board);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_move(&self, params: AutoplacerStepParams) -> Vector2<i64> {
|
||||||
|
crate::profile_function!();
|
||||||
|
let dx_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
||||||
|
let dy_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
||||||
|
Vector2::new(
|
||||||
|
dx_gaussian.sample(&mut rand::rng()) as i64,
|
||||||
|
dy_gaussian.sample(&mut rand::rng()) as i64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accept_move(&mut self, board: &mut Board) {
|
||||||
|
crate::profile_function!();
|
||||||
|
self.origin_delta = self.origin_delta.clone().merge_delta(board.flush_delta());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reject_move(&mut self, board: &mut Board) {
|
||||||
|
crate::profile_function!();
|
||||||
|
board.reset_delta();
|
||||||
|
}
|
||||||
|
|
||||||
/*fn cost(&self, board: &Board, params: AutoplacerStepParams) -> f64 {
|
/*fn cost(&self, board: &Board, params: AutoplacerStepParams) -> f64 {
|
||||||
self.components
|
self.components
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -130,26 +154,42 @@ impl Autoplacer {
|
||||||
&self,
|
&self,
|
||||||
board: &Board,
|
board: &Board,
|
||||||
component: ComponentId,
|
component: ComponentId,
|
||||||
params: AutoplacerStepParams,
|
_params: AutoplacerStepParams,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let layout = board.layout();
|
crate::profile_function!();
|
||||||
|
|
||||||
let repulsion_cost: i64 = layout
|
let layout = board.layout();
|
||||||
.locate_component_repulsions(component, Orientation::Oblique)
|
let repulsion_cost = self.repulsion_cost(layout, component);
|
||||||
.map(|vector| vector.x.abs() + vector.y.abs())
|
let attraction_cost = self.attraction_cost(layout, component);
|
||||||
.sum();
|
let retention_cost = self.retention_cost(layout, component);
|
||||||
let attraction_cost: f64 = layout
|
|
||||||
.component_attractions(component)
|
|
||||||
.map(|vector| 1.0 / (1.0 + (vector.x.abs() + vector.y.abs()) as f64))
|
|
||||||
.sum();
|
|
||||||
let retention_cost: i64 = layout
|
|
||||||
.component_retentions(component)
|
|
||||||
.map(|vector| 100 * (vector.x.abs() + vector.y.abs()))
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
repulsion_cost as f64 + attraction_cost + retention_cost as f64
|
repulsion_cost as f64 + attraction_cost + retention_cost as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn repulsion_cost(&self, layout: &Layout, component: ComponentId) -> i64 {
|
||||||
|
crate::profile_function!();
|
||||||
|
layout
|
||||||
|
.locate_component_repulsions(component, Orientation::Oblique)
|
||||||
|
.map(|vector| vector.x.abs() + vector.y.abs())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attraction_cost(&self, layout: &Layout, component: ComponentId) -> f64 {
|
||||||
|
crate::profile_function!();
|
||||||
|
layout
|
||||||
|
.component_attractions(component)
|
||||||
|
.map(|vector| 1.0 / (1.0 + (vector.x.abs() + vector.y.abs()) as f64))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retention_cost(&self, layout: &Layout, component: ComponentId) -> i64 {
|
||||||
|
crate::profile_function!();
|
||||||
|
layout
|
||||||
|
.component_retentions(component)
|
||||||
|
.map(|vector| 100 * (vector.x.abs() + vector.y.abs()))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
/*fn step_component_with_params(
|
/*fn step_component_with_params(
|
||||||
&mut self,
|
&mut self,
|
||||||
component: ComponentId,
|
component: ComponentId,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ impl Board {
|
||||||
selection: &[ComponentId],
|
selection: &[ComponentId],
|
||||||
translation: Vector2<i64>,
|
translation: Vector2<i64>,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
self.layout.move_components_by(selection, translation);
|
self.layout.move_components_by(selection, translation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ impl Pin {
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
pub fn pin_centroid(&self, pin_id: PinId) -> Vector2<i64> {
|
pub fn pin_centroid(&self, pin_id: PinId) -> Vector2<i64> {
|
||||||
|
crate::profile_function!();
|
||||||
let pin = self.pin(pin_id);
|
let pin = self.pin(pin_id);
|
||||||
let mut sum = Vector2::new(0, 0);
|
let mut sum = Vector2::new(0, 0);
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ impl Polygon {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn center(&self) -> Vector2<i64> {
|
pub fn center(&self) -> Vector2<i64> {
|
||||||
|
crate::profile_function!();
|
||||||
Vector2::<i64>::polygon_centroid(&self.vertices)
|
Vector2::<i64>::polygon_centroid(&self.vertices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_components_by(&mut self, ids: &[ComponentId], translation: Vector2<i64>) {
|
pub fn move_components_by(&mut self, ids: &[ComponentId], translation: Vector2<i64>) {
|
||||||
|
crate::profile_function!();
|
||||||
for id in ids {
|
for id in ids {
|
||||||
let component = self.components[id.index()].clone();
|
let component = self.components[id.index()].clone();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
mod profiler;
|
||||||
|
|
||||||
mod autoplacer;
|
mod autoplacer;
|
||||||
mod autorouter;
|
mod autorouter;
|
||||||
pub mod board;
|
pub mod board;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! profile_scope {
|
||||||
|
($name:expr) => {
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
puffin::profile_scope!($name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! profile_function {
|
||||||
|
() => {
|
||||||
|
#[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))]
|
||||||
|
puffin::profile_function!();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -247,6 +247,7 @@ macro_rules! impl_polygon_centroid {
|
||||||
($type:ty) => {
|
($type:ty) => {
|
||||||
impl Vector2<$type> {
|
impl Vector2<$type> {
|
||||||
pub fn polygon_centroid(polygon: &[Vector2<$type>]) -> Self {
|
pub fn polygon_centroid(polygon: &[Vector2<$type>]) -> Self {
|
||||||
|
crate::profile_function!();
|
||||||
let mut sum = Vector2::new(0 as $type, 0 as $type);
|
let mut sum = Vector2::new(0 as $type, 0 as $type);
|
||||||
|
|
||||||
for vertex in polygon.iter() {
|
for vertex in polygon.iter() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue