Record timing while applying map edits. Even in release mode, this is a long inexplicable pause for large-scale bike network changes. A loading screen is better.

This commit is contained in:
Dustin Carlino 2021-10-05 11:35:37 -07:00
parent 0fc1af0dd9
commit 309db511c2
6 changed files with 82 additions and 64 deletions

View File

@ -499,7 +499,9 @@ impl State<App> for SaveEdits {
"Save" => { "Save" => {
let mut edits = app.primary.map.get_edits().clone(); let mut edits = app.primary.map.get_edits().clone();
edits.edits_name = self.current_name.clone(); edits.edits_name = self.current_name.clone();
app.primary.map.must_apply_edits(edits); app.primary
.map
.must_apply_edits(edits, &mut Timer::throwaway());
app.primary.map.save_edits(); app.primary.map.save_edits();
if self.reset { if self.reset {
apply_map_edits(ctx, app, app.primary.map.new_edits()); apply_map_edits(ctx, app, app.primary.map.new_edits());
@ -687,8 +689,7 @@ fn make_topcenter(ctx: &mut EventCtx, app: &App) -> Panel {
} }
pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) { pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
let mut timer = Timer::new("apply map edits"); ctx.loading_screen("apply map edits", |ctx, timer| {
if app.primary.unedited_map.is_none() { if app.primary.unedited_map.is_none() {
timer.start("save unedited map"); timer.start("save unedited map");
assert!(app.primary.map.get_edits().commands.is_empty()); assert!(app.primary.map.get_edits().commands.is_empty());
@ -697,14 +698,14 @@ pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
} }
timer.start("edit map"); timer.start("edit map");
let effects = app.primary.map.must_apply_edits(edits); let effects = app.primary.map.must_apply_edits(edits, timer);
timer.stop("edit map"); timer.stop("edit map");
if !effects.changed_roads.is_empty() || !effects.changed_intersections.is_empty() { if !effects.changed_roads.is_empty() || !effects.changed_intersections.is_empty() {
app.primary app.primary
.draw_map .draw_map
.draw_all_unzoomed_roads_and_intersections = .draw_all_unzoomed_roads_and_intersections =
DrawMap::regenerate_unzoomed_layer(&app.primary.map, &app.cs, ctx, &mut timer); DrawMap::regenerate_unzoomed_layer(&app.primary.map, &app.cs, ctx, timer);
} }
for l in effects.deleted_lanes { for l in effects.deleted_lanes {
@ -738,6 +739,7 @@ pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
// Autosave // Autosave
app.primary.map.save_edits(); app.primary.map.save_edits();
});
} }
pub fn can_edit_lane(app: &App, l: LaneID) -> bool { pub fn can_edit_lane(app: &App, l: LaneID) -> bool {

View File

@ -1,5 +1,6 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use abstutil::Timer;
use map_gui::tools::PopupMsg; use map_gui::tools::PopupMsg;
use map_model::{connectivity, EditCmd, PathConstraints}; use map_model::{connectivity, EditCmd, PathConstraints};
use widgetry::{EventCtx, State}; use widgetry::{EventCtx, State};
@ -21,11 +22,15 @@ pub fn check_sidewalk_connectivity(
let mut edits = orig_edits.clone(); let mut edits = orig_edits.clone();
edits.commands.push(cmd); edits.commands.push(cmd);
app.primary.map.try_apply_edits(edits); app.primary
.map
.try_apply_edits(edits, &mut Timer::throwaway());
let (_, disconnected_after) = let (_, disconnected_after) =
connectivity::find_scc(&app.primary.map, PathConstraints::Pedestrian); connectivity::find_scc(&app.primary.map, PathConstraints::Pedestrian);
app.primary.map.must_apply_edits(orig_edits); app.primary
.map
.must_apply_edits(orig_edits, &mut Timer::throwaway());
let newly_disconnected = disconnected_after let newly_disconnected = disconnected_after
.difference(&disconnected_before) .difference(&disconnected_before)
@ -68,7 +73,9 @@ pub fn check_blackholes(
let mut edits = orig_edits.clone(); let mut edits = orig_edits.clone();
edits.commands.push(cmd); edits.commands.push(cmd);
app.primary.map.try_apply_edits(edits); app.primary
.map
.try_apply_edits(edits, &mut Timer::throwaway());
let mut newly_disconnected = BTreeSet::new(); let mut newly_disconnected = BTreeSet::new();
for l in connectivity::find_scc(&app.primary.map, PathConstraints::Car).1 { for l in connectivity::find_scc(&app.primary.map, PathConstraints::Car).1 {
@ -81,7 +88,9 @@ pub fn check_blackholes(
newly_disconnected.insert(l); newly_disconnected.insert(l);
} }
} }
app.primary.map.must_apply_edits(orig_edits); app.primary
.map
.must_apply_edits(orig_edits, &mut Timer::throwaway());
if newly_disconnected.is_empty() { if newly_disconnected.is_empty() {
return None; return None;

View File

@ -202,7 +202,7 @@ fn handle_command(
old: map.get_i_edit(id), old: map.get_i_edit(id),
new: EditIntersection::TrafficSignal(ts.export(map)), new: EditIntersection::TrafficSignal(ts.export(map)),
}); });
map.must_apply_edits(edits); map.must_apply_edits(edits, &mut Timer::throwaway());
map.recalculate_pathfinding_after_edits(&mut Timer::throwaway()); map.recalculate_pathfinding_after_edits(&mut Timer::throwaway());
Ok(format!("{} has been updated", id)) Ok(format!("{} has been updated", id))
@ -476,7 +476,7 @@ impl LoadSim {
let mut map = Map::load_synchronously(scenario.map_name.path(), timer); let mut map = Map::load_synchronously(scenario.map_name.path(), timer);
if let Some(perma) = self.edits.clone() { if let Some(perma) = self.edits.clone() {
let edits = perma.into_edits(&map).unwrap(); let edits = perma.into_edits(&map).unwrap();
map.must_apply_edits(edits); map.must_apply_edits(edits, timer);
map.recalculate_pathfinding_after_edits(timer); map.recalculate_pathfinding_after_edits(timer);
} }

View File

@ -815,17 +815,22 @@ impl Map {
} }
/// Returns (changed_roads, deleted_lanes, deleted_turns, added_turns, changed_intersections) /// Returns (changed_roads, deleted_lanes, deleted_turns, added_turns, changed_intersections)
pub fn must_apply_edits(&mut self, new_edits: MapEdits) -> EditEffects { pub fn must_apply_edits(&mut self, new_edits: MapEdits, timer: &mut Timer) -> EditEffects {
self.apply_edits(new_edits, true) self.apply_edits(new_edits, true, timer)
} }
pub fn try_apply_edits(&mut self, new_edits: MapEdits) { pub fn try_apply_edits(&mut self, new_edits: MapEdits, timer: &mut Timer) {
self.apply_edits(new_edits, false); self.apply_edits(new_edits, false, timer);
} }
// new_edits don't necessarily have to be valid; this could be used for speculatively testing // new_edits don't necessarily have to be valid; this could be used for speculatively testing
// edits. Doesn't update pathfinding yet. // edits. Doesn't update pathfinding yet.
fn apply_edits(&mut self, mut new_edits: MapEdits, enforce_valid: bool) -> EditEffects { fn apply_edits(
&mut self,
mut new_edits: MapEdits,
enforce_valid: bool,
timer: &mut Timer,
) -> EditEffects {
self.edits_generation += 1; self.edits_generation += 1;
let mut effects = EditEffects { let mut effects = EditEffects {
@ -855,8 +860,9 @@ impl Map {
} }
} }
// Undo existing edits timer.start_iter("undo old edits", self.edits.commands.len() - start_at_idx);
for _ in start_at_idx..self.edits.commands.len() { for _ in start_at_idx..self.edits.commands.len() {
timer.next();
self.edits self.edits
.commands .commands
.pop() .pop()
@ -865,8 +871,9 @@ impl Map {
.apply(&mut effects, self); .apply(&mut effects, self);
} }
// Apply new edits. timer.start_iter("apply new edits", new_edits.commands.len() - start_at_idx);
for cmd in &new_edits.commands[start_at_idx..] { for cmd in &new_edits.commands[start_at_idx..] {
timer.next();
cmd.apply(&mut effects, self); cmd.apply(&mut effects, self);
} }

View File

@ -74,7 +74,7 @@ impl SimFlags {
timer, timer,
) { ) {
Ok(edits) => { Ok(edits) => {
map.must_apply_edits(edits); map.must_apply_edits(edits, timer);
map.recalculate_pathfinding_after_edits(timer); map.recalculate_pathfinding_after_edits(timer);
} }
Err(err) => { Err(err) => {

View File

@ -26,7 +26,7 @@ fn main() {
{ {
let mut edits = map.get_edits().clone(); let mut edits = map.get_edits().clone();
edits.edits_name = "traffic_seitan".to_string(); edits.edits_name = "traffic_seitan".to_string();
map.must_apply_edits(edits); map.must_apply_edits(edits, &mut timer);
map.recalculate_pathfinding_after_edits(&mut timer); map.recalculate_pathfinding_after_edits(&mut timer);
sim.handle_live_edits(&map, &mut timer); sim.handle_live_edits(&map, &mut timer);
} }
@ -36,7 +36,7 @@ fn main() {
})) { })) {
let mut edits = map.get_edits().clone(); let mut edits = map.get_edits().clone();
edits.edits_name = "traffic_seitan_crash".to_string(); edits.edits_name = "traffic_seitan_crash".to_string();
map.must_apply_edits(edits); map.must_apply_edits(edits, &mut timer);
map.save_edits(); map.save_edits();
println!("Crashed at {}", sim.time()); println!("Crashed at {}", sim.time());
@ -58,7 +58,7 @@ fn run(map: &mut Map, sim: &mut Sim, rng: &mut XorShiftRng, timer: &mut Timer) {
nuke_random_parking(map, rng, &mut edits); nuke_random_parking(map, rng, &mut edits);
alter_turn_destinations(sim, map, rng, &mut edits); alter_turn_destinations(sim, map, rng, &mut edits);
map.must_apply_edits(edits); map.must_apply_edits(edits, timer);
map.recalculate_pathfinding_after_edits(timer); map.recalculate_pathfinding_after_edits(timer);
sim.handle_live_edited_traffic_signals(map); sim.handle_live_edited_traffic_signals(map);
sim.handle_live_edits(map, timer); sim.handle_live_edits(map, timer);