lazily render lanes, same as road labels. greatly speeds up start-up times (helps with #125), doesnt melt the GPU on large maps. also paves the way for dynamically adjusting z-order rendering (for #126)

because we no longer try to upload massive gobs of data to the GPU at
once, huge_seattle on my system goes from initially loading in about a
minute down to 11s
This commit is contained in:
Dustin Carlino 2020-07-05 11:30:34 -07:00
parent f818b3877e
commit 422889e543
6 changed files with 150 additions and 186 deletions

View File

@ -497,7 +497,6 @@ impl ShowObject for ShowEverything {
#[derive(Clone)]
pub struct Flags {
pub sim_flags: SimFlags,
pub draw_lane_markings: bool,
// Number of agents to generate when requested. If unspecified, trips to/from borders will be
// included.
pub num_agents: Option<usize>,
@ -526,7 +525,7 @@ impl PerMap {
mem.reset("Map and Sim", timer);
timer.start("draw_map");
let draw_map = DrawMap::new(&map, &flags, cs, ctx, timer);
let draw_map = DrawMap::new(&map, cs, ctx, timer);
timer.stop("draw_map");
mem.reset("DrawMap", timer);

View File

@ -15,7 +15,7 @@ use crate::debug::DebugMode;
use crate::game::{msg, State, Transition, WizardState};
use crate::helpers::ID;
use crate::managed::{WrappedComposite, WrappedOutcome};
use crate::render::{DrawIntersection, DrawLane, DrawRoad};
use crate::render::{DrawIntersection, DrawRoad};
use crate::sandbox::{GameplayMode, SandboxMode, TimeWarpScreen};
use abstutil::Timer;
use ezgui::{
@ -508,15 +508,7 @@ pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
// 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.all_lanes() {
let lane = app.primary.map.get_l(l);
app.primary.draw_map.lanes[l.0] = DrawLane::new(
lane,
&app.primary.map,
app.primary.current_flags.draw_lane_markings,
&app.cs,
&mut timer,
)
.finish(ctx.prerender, &app.cs, lane);
app.primary.draw_map.lanes[l.0].clear_rendering();
}
}

View File

@ -30,7 +30,6 @@ fn main() {
let mut flags = Flags {
sim_flags: SimFlags::from_args(&mut args),
draw_lane_markings: !args.enabled("--dont_draw_lane_markings"),
num_agents: args.optional_parse("--num_agents", |s| s.parse()),
};
let mut opts = options::Options::default();

View File

@ -1,24 +1,131 @@
use crate::app::App;
use crate::colors::ColorScheme;
use crate::helpers::ID;
use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS};
use abstutil::Timer;
use ezgui::{Drawable, GeomBatch, GfxCtx, Prerender, RewriteColor};
use ezgui::{Drawable, GeomBatch, GfxCtx, RewriteColor};
use geom::{Angle, ArrowCap, Distance, Line, PolyLine, Polygon, Pt2D};
use map_model::{Lane, LaneID, LaneType, Map, Road, TurnType, PARKING_SPOT_LENGTH};
use std::cell::RefCell;
// Split into two phases like this, because AlmostDrawLane can be created in parallel, but GPU
// upload has to be serial.
pub struct AlmostDrawLane {
pub struct DrawLane {
pub id: LaneID,
polygon: Polygon,
pub polygon: Polygon,
zorder: isize,
draw_default: GeomBatch,
draw_default: RefCell<Option<Drawable>>,
}
impl AlmostDrawLane {
pub fn finish(mut self, prerender: &Prerender, _: &ColorScheme, lane: &Lane) -> DrawLane {
// Need prerender to load the (cached) SVGs
impl DrawLane {
pub fn new(lane: &Lane, map: &Map) -> DrawLane {
DrawLane {
id: lane.id,
polygon: lane.lane_center_pts.make_polygons(lane.width),
zorder: map.get_r(lane.parent).zorder,
draw_default: RefCell::new(None),
}
}
pub fn clear_rendering(&mut self) {
*self.draw_default.borrow_mut() = None;
}
fn render(&self, g: &mut GfxCtx, app: &App) -> Drawable {
let map = &app.primary.map;
let lane = map.get_l(self.id);
let road = map.get_r(lane.parent);
let mut draw = GeomBatch::new();
let mut timer = Timer::throwaway();
if lane.lane_type != LaneType::LightRail {
draw.push(
match lane.lane_type {
LaneType::Driving => app.cs.driving_lane,
LaneType::Bus => app.cs.bus_lane,
LaneType::Parking => app.cs.parking_lane,
LaneType::Sidewalk => app.cs.sidewalk,
LaneType::Biking => app.cs.bike_lane,
LaneType::SharedLeftTurn => app.cs.driving_lane,
LaneType::Construction => app.cs.parking_lane,
LaneType::LightRail => unreachable!(),
},
self.polygon.clone(),
);
}
match lane.lane_type {
LaneType::Sidewalk => {
draw.extend(app.cs.sidewalk_lines, calculate_sidewalk_lines(lane));
}
LaneType::Parking => {
draw.extend(
app.cs.general_road_marking,
calculate_parking_lines(map, lane),
);
}
LaneType::Driving | LaneType::Bus => {
draw.extend(
app.cs.general_road_marking,
calculate_driving_lines(map, lane, road, &mut timer),
);
draw.extend(
app.cs.general_road_marking,
calculate_turn_markings(map, lane, &mut timer),
);
draw.extend(
app.cs.general_road_marking,
calculate_one_way_markings(lane, road),
);
}
LaneType::Biking => {}
LaneType::SharedLeftTurn => {
draw.push(
app.cs.road_center_line,
lane.lane_center_pts
.shift_right(lane.width / 2.0)
.get(&mut timer)
.make_polygons(Distance::meters(0.25)),
);
draw.push(
app.cs.road_center_line,
lane.lane_center_pts
.shift_left(lane.width / 2.0)
.get(&mut timer)
.make_polygons(Distance::meters(0.25)),
);
}
LaneType::Construction => {}
LaneType::LightRail => {
let track_width = lane.width / 4.0;
draw.push(
app.cs.light_rail_track,
lane.lane_center_pts
.shift_right((lane.width - track_width) / 2.5)
.get(&mut timer)
.make_polygons(track_width),
);
draw.push(
app.cs.light_rail_track,
lane.lane_center_pts
.shift_left((lane.width - track_width) / 2.5)
.get(&mut timer)
.make_polygons(track_width),
);
// Start away from the intersections
let tile_every = Distance::meters(3.0);
let mut dist_along = tile_every;
while dist_along < lane.lane_center_pts.length() - tile_every {
let (pt, angle) = lane.dist_along(dist_along);
// Reuse perp_line. Project away an arbitrary amount
let pt2 = pt.project_away(Distance::meters(1.0), angle);
draw.push(
app.cs.light_rail_track,
perp_line(Line::new(pt, pt2), lane.width).make_polygons(track_width),
);
dist_along += tile_every;
}
}
}
if lane.is_bus() || lane.is_biking() || lane.lane_type == LaneType::Construction {
let buffer = Distance::meters(2.0);
let btwn = Distance::meters(30.0);
@ -28,9 +135,9 @@ impl AlmostDrawLane {
while dist + buffer <= len {
let (pt, angle) = lane.lane_center_pts.dist_along(dist);
if lane.is_bus() {
self.draw_default.append(
draw.append(
GeomBatch::mapspace_svg(
prerender,
g.prerender,
"../data/system/assets/map/bus_only.svg",
)
.scale(0.06)
@ -38,17 +145,20 @@ impl AlmostDrawLane {
.rotate(angle.shortest_rotation_towards(Angle::new_degs(-90.0))),
);
} else if lane.is_biking() {
self.draw_default.append(
GeomBatch::mapspace_svg(prerender, "../data/system/assets/meters/bike.svg")
.scale(0.06)
.centered_on(pt)
.rotate(angle.shortest_rotation_towards(Angle::new_degs(-90.0))),
draw.append(
GeomBatch::mapspace_svg(
g.prerender,
"../data/system/assets/meters/bike.svg",
)
.scale(0.06)
.centered_on(pt)
.rotate(angle.shortest_rotation_towards(Angle::new_degs(-90.0))),
);
} else if lane.lane_type == LaneType::Construction {
// TODO Still not quite centered right, but close enough
self.draw_default.append(
draw.append(
GeomBatch::mapspace_svg(
prerender,
g.prerender,
"../data/system/assets/map/under_construction.svg",
)
.scale(0.05)
@ -63,137 +173,15 @@ impl AlmostDrawLane {
}
}
if self.zorder < 0 {
self.draw_default = self.draw_default.color(RewriteColor::ChangeAlpha(0.5));
}
DrawLane {
id: self.id,
polygon: self.polygon,
zorder: self.zorder,
draw_default: prerender.upload(self.draw_default),
}
}
}
pub struct DrawLane {
pub id: LaneID,
pub polygon: Polygon,
zorder: isize,
draw_default: Drawable,
}
impl DrawLane {
pub fn new(
lane: &Lane,
map: &Map,
draw_lane_markings: bool,
cs: &ColorScheme,
timer: &mut Timer,
) -> AlmostDrawLane {
let road = map.get_r(lane.parent);
let polygon = lane.lane_center_pts.make_polygons(lane.width);
let mut draw = GeomBatch::new();
if lane.lane_type != LaneType::LightRail {
draw.push(
match lane.lane_type {
LaneType::Driving => cs.driving_lane,
LaneType::Bus => cs.bus_lane,
LaneType::Parking => cs.parking_lane,
LaneType::Sidewalk => cs.sidewalk,
LaneType::Biking => cs.bike_lane,
LaneType::SharedLeftTurn => cs.driving_lane,
LaneType::Construction => cs.parking_lane,
LaneType::LightRail => unreachable!(),
},
polygon.clone(),
);
}
if draw_lane_markings {
match lane.lane_type {
LaneType::Sidewalk => {
draw.extend(cs.sidewalk_lines, calculate_sidewalk_lines(lane));
}
LaneType::Parking => {
draw.extend(cs.general_road_marking, calculate_parking_lines(map, lane));
}
LaneType::Driving | LaneType::Bus => {
draw.extend(
cs.general_road_marking,
calculate_driving_lines(map, lane, road, timer),
);
draw.extend(
cs.general_road_marking,
calculate_turn_markings(map, lane, timer),
);
draw.extend(
cs.general_road_marking,
calculate_one_way_markings(lane, road),
);
}
LaneType::Biking => {}
LaneType::SharedLeftTurn => {
draw.push(
cs.road_center_line,
lane.lane_center_pts
.shift_right(lane.width / 2.0)
.get(timer)
.make_polygons(Distance::meters(0.25)),
);
draw.push(
cs.road_center_line,
lane.lane_center_pts
.shift_left(lane.width / 2.0)
.get(timer)
.make_polygons(Distance::meters(0.25)),
);
}
LaneType::Construction => {}
LaneType::LightRail => {
let track_width = lane.width / 4.0;
draw.push(
cs.light_rail_track,
lane.lane_center_pts
.shift_right((lane.width - track_width) / 2.5)
.get(timer)
.make_polygons(track_width),
);
draw.push(
cs.light_rail_track,
lane.lane_center_pts
.shift_left((lane.width - track_width) / 2.5)
.get(timer)
.make_polygons(track_width),
);
// Start away from the intersections
let tile_every = Distance::meters(3.0);
let mut dist_along = tile_every;
while dist_along < lane.lane_center_pts.length() - tile_every {
let (pt, angle) = lane.dist_along(dist_along);
// Reuse perp_line. Project away an arbitrary amount
let pt2 = pt.project_away(Distance::meters(1.0), angle);
draw.push(
cs.light_rail_track,
perp_line(Line::new(pt, pt2), lane.width).make_polygons(track_width),
);
dist_along += tile_every;
}
}
};
}
if road.zone.is_some() {
draw.push(cs.private_road.alpha(0.5), polygon.clone());
draw.push(app.cs.private_road.alpha(0.5), self.polygon.clone());
}
AlmostDrawLane {
id: lane.id,
polygon,
zorder: road.zorder,
draw_default: draw,
if self.zorder < 0 {
draw = draw.color(RewriteColor::ChangeAlpha(0.5));
}
g.upload(draw)
}
}
@ -202,8 +190,14 @@ impl Renderable for DrawLane {
ID::Lane(self.id)
}
fn draw(&self, g: &mut GfxCtx, _: &App, _: &DrawOptions) {
g.redraw(&self.draw_default);
fn draw(&self, g: &mut GfxCtx, app: &App, _: &DrawOptions) {
// Lazily calculate, because these are expensive to all do up-front, and most players won't
// exhaustively see every lane during a single session
let mut draw = self.draw_default.borrow_mut();
if draw.is_none() {
*draw = Some(self.render(g, app));
}
g.redraw(draw.as_ref().unwrap());
}
fn get_outline(&self, map: &Map) -> Polygon {

View File

@ -1,4 +1,4 @@
use crate::app::{App, Flags};
use crate::app::App;
use crate::colors::ColorScheme;
use crate::helpers::ID;
use crate::render::building::DrawBuilding;
@ -46,13 +46,7 @@ pub struct DrawMap {
}
impl DrawMap {
pub fn new(
map: &Map,
flags: &Flags,
cs: &ColorScheme,
ctx: &EventCtx,
timer: &mut Timer,
) -> DrawMap {
pub fn new(map: &Map, cs: &ColorScheme, ctx: &EventCtx, timer: &mut Timer) -> DrawMap {
let mut roads: Vec<DrawRoad> = Vec::new();
timer.start_iter("make DrawRoads", map.all_roads().len());
for r in map.all_roads() {
@ -85,24 +79,11 @@ impl DrawMap {
let draw_all_thick_roads = all_roads.upload(ctx);
timer.stop("generate thick roads");
let almost_lanes =
timer.parallelize("prepare DrawLanes", map.all_lanes().iter().collect(), |l| {
DrawLane::new(
l,
map,
flags.draw_lane_markings,
cs,
// TODO Really parallelize should give us something thread-safe that can at
// least take notes.
&mut Timer::throwaway(),
)
});
timer.start_iter("finalize DrawLanes", almost_lanes.len());
let mut lanes: Vec<DrawLane> = Vec::new();
for almost in almost_lanes {
timer.start_iter("make DrawLanes", map.all_lanes().len());
for l in map.all_lanes() {
timer.next();
let lane = map.get_l(almost.id);
lanes.push(almost.finish(ctx.prerender, cs, lane));
lanes.push(DrawLane::new(l, map));
}
let mut intersections: Vec<DrawIntersection> = Vec::new();

View File

@ -19,7 +19,6 @@ pub use crate::render::area::DrawArea;
use crate::render::bike::DrawBike;
use crate::render::car::DrawCar;
pub use crate::render::intersection::{calculate_corners, DrawIntersection};
pub use crate::render::lane::DrawLane;
pub use crate::render::map::{AgentCache, AgentColorScheme, DrawMap};
pub use crate::render::pedestrian::{DrawPedCrowd, DrawPedestrian};
pub use crate::render::road::DrawRoad;