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" => {
let mut edits = app.primary.map.get_edits().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();
if self.reset {
apply_map_edits(ctx, app, app.primary.map.new_edits());
@ -687,57 +689,57 @@ fn make_topcenter(ctx: &mut EventCtx, app: &App) -> Panel {
}
pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
let mut timer = Timer::new("apply map edits");
if app.primary.unedited_map.is_none() {
timer.start("save unedited map");
assert!(app.primary.map.get_edits().commands.is_empty());
app.primary.unedited_map = Some(app.primary.map.clone());
timer.stop("save unedited map");
}
timer.start("edit map");
let effects = app.primary.map.must_apply_edits(edits);
timer.stop("edit map");
if !effects.changed_roads.is_empty() || !effects.changed_intersections.is_empty() {
app.primary
.draw_map
.draw_all_unzoomed_roads_and_intersections =
DrawMap::regenerate_unzoomed_layer(&app.primary.map, &app.cs, ctx, &mut timer);
}
for l in effects.deleted_lanes {
app.primary.draw_map.delete_lane(l);
}
for r in effects.changed_roads {
let road = app.primary.map.get_r(r);
app.primary.draw_map.recreate_road(road, &app.primary.map);
// An edit to one lane potentially affects markings in all lanes in the same road, because
// of one-way markings, driving lines, etc.
for l in &road.lanes {
app.primary.draw_map.create_lane(l.id, &app.primary.map);
ctx.loading_screen("apply map edits", |ctx, timer| {
if app.primary.unedited_map.is_none() {
timer.start("save unedited map");
assert!(app.primary.map.get_edits().commands.is_empty());
app.primary.unedited_map = Some(app.primary.map.clone());
timer.stop("save unedited map");
}
}
for i in effects.changed_intersections {
app.primary
.draw_map
.recreate_intersection(i, &app.primary.map);
}
timer.start("edit map");
let effects = app.primary.map.must_apply_edits(edits, timer);
timer.stop("edit map");
for pl in effects.changed_parking_lots {
app.primary.draw_map.get_pl(pl).clear_rendering();
}
if !effects.changed_roads.is_empty() || !effects.changed_intersections.is_empty() {
app.primary
.draw_map
.draw_all_unzoomed_roads_and_intersections =
DrawMap::regenerate_unzoomed_layer(&app.primary.map, &app.cs, ctx, timer);
}
if app.primary.layer.as_ref().and_then(|l| l.name()) == Some("map edits") {
app.primary.layer = Some(Box::new(crate::layer::map::Static::edits(ctx, app)));
}
for l in effects.deleted_lanes {
app.primary.draw_map.delete_lane(l);
}
// Autosave
app.primary.map.save_edits();
for r in effects.changed_roads {
let road = app.primary.map.get_r(r);
app.primary.draw_map.recreate_road(road, &app.primary.map);
// An edit to one lane potentially affects markings in all lanes in the same road, because
// of one-way markings, driving lines, etc.
for l in &road.lanes {
app.primary.draw_map.create_lane(l.id, &app.primary.map);
}
}
for i in effects.changed_intersections {
app.primary
.draw_map
.recreate_intersection(i, &app.primary.map);
}
for pl in effects.changed_parking_lots {
app.primary.draw_map.get_pl(pl).clear_rendering();
}
if app.primary.layer.as_ref().and_then(|l| l.name()) == Some("map edits") {
app.primary.layer = Some(Box::new(crate::layer::map::Static::edits(ctx, app)));
}
// Autosave
app.primary.map.save_edits();
});
}
pub fn can_edit_lane(app: &App, l: LaneID) -> bool {

View File

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

View File

@ -202,7 +202,7 @@ fn handle_command(
old: map.get_i_edit(id),
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());
Ok(format!("{} has been updated", id))
@ -476,7 +476,7 @@ impl LoadSim {
let mut map = Map::load_synchronously(scenario.map_name.path(), timer);
if let Some(perma) = self.edits.clone() {
let edits = perma.into_edits(&map).unwrap();
map.must_apply_edits(edits);
map.must_apply_edits(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)
pub fn must_apply_edits(&mut self, new_edits: MapEdits) -> EditEffects {
self.apply_edits(new_edits, true)
pub fn must_apply_edits(&mut self, new_edits: MapEdits, timer: &mut Timer) -> EditEffects {
self.apply_edits(new_edits, true, timer)
}
pub fn try_apply_edits(&mut self, new_edits: MapEdits) {
self.apply_edits(new_edits, false);
pub fn try_apply_edits(&mut self, new_edits: MapEdits, timer: &mut Timer) {
self.apply_edits(new_edits, false, timer);
}
// new_edits don't necessarily have to be valid; this could be used for speculatively testing
// 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;
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() {
timer.next();
self.edits
.commands
.pop()
@ -865,8 +871,9 @@ impl Map {
.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..] {
timer.next();
cmd.apply(&mut effects, self);
}

View File

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

View File

@ -26,7 +26,7 @@ fn main() {
{
let mut edits = map.get_edits().clone();
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);
sim.handle_live_edits(&map, &mut timer);
}
@ -36,7 +36,7 @@ fn main() {
})) {
let mut edits = map.get_edits().clone();
edits.edits_name = "traffic_seitan_crash".to_string();
map.must_apply_edits(edits);
map.must_apply_edits(edits, &mut timer);
map.save_edits();
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);
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);
sim.handle_live_edited_traffic_signals(map);
sim.handle_live_edits(map, timer);