Skip to content

Commit

Permalink
Memory leak when rendering (camera / UI) in PG map with multiple scen…
Browse files Browse the repository at this point in the history
…arios (#656)

* update a minimal reproducible script

* remove cache height map and semantic map in Map instance

* rename test script
  • Loading branch information
pengzhenghao authored Feb 21, 2024
1 parent c326ae5 commit 6504392
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 107 deletions.
214 changes: 109 additions & 105 deletions metadrive/component/map/base_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ def __init__(self, map_config: dict = None, random_seed=None):
self.detach_from_world()

# save a backup
self._semantic_map = None
self._height_map = None
# self._semantic_map = None
# self._height_map = None

def _generate(self):
"""Key function! Please overwrite it! This func aims at fill the self.road_network adn self.blocks"""
Expand Down Expand Up @@ -105,12 +105,12 @@ def num_blocks(self):

def destroy(self):
self.detach_from_world()
if self._semantic_map is not None:
del self._semantic_map
self._semantic_map = None
if self._height_map is not None:
del self._height_map
self._height_map = None
# if self._semantic_map is not None:
# del self._semantic_map
# self._semantic_map = None
# if self._height_map is not None:
# del self._height_map
# self._height_map = None

for block in self.blocks:
block.destroy()
Expand Down Expand Up @@ -203,77 +203,79 @@ def get_semantic_map(
:return: semantic map
"""
center_p = center_point
if self._semantic_map is None:
all_lanes = self.get_map_features(interval=line_sample_interval)
polygons = []
polylines = []

points_to_skip = math.floor(PGDrivableAreaProperty.STRIPE_LENGTH * 2 / line_sample_interval)
for obj in all_lanes.values():
if MetaDriveType.is_lane(obj["type"]) and "lane" in layer:
polygons.append((obj["polygon"], MapTerrainSemanticColor.get_color(obj["type"])))
elif "lane_line" in layer and (MetaDriveType.is_road_line(obj["type"])
or MetaDriveType.is_road_boundary_line(obj["type"])):
if MetaDriveType.is_broken_line(obj["type"]):
for index in range(0, len(obj["polyline"]) - 1, points_to_skip * 2):
if index + points_to_skip < len(obj["polyline"]):
polylines.append(
(
[obj["polyline"][index], obj["polyline"][index + points_to_skip]],
MapTerrainSemanticColor.get_color(obj["type"])
)

# if self._semantic_map is None:
all_lanes = self.get_map_features(interval=line_sample_interval)
polygons = []
polylines = []

points_to_skip = math.floor(PGDrivableAreaProperty.STRIPE_LENGTH * 2 / line_sample_interval)
for obj in all_lanes.values():
if MetaDriveType.is_lane(obj["type"]) and "lane" in layer:
polygons.append((obj["polygon"], MapTerrainSemanticColor.get_color(obj["type"])))
elif "lane_line" in layer and (MetaDriveType.is_road_line(obj["type"])
or MetaDriveType.is_road_boundary_line(obj["type"])):
if MetaDriveType.is_broken_line(obj["type"]):
for index in range(0, len(obj["polyline"]) - 1, points_to_skip * 2):
if index + points_to_skip < len(obj["polyline"]):
polylines.append(
(
[obj["polyline"][index], obj["polyline"][index + points_to_skip]],
MapTerrainSemanticColor.get_color(obj["type"])
)
else:
polylines.append((obj["polyline"], MapTerrainSemanticColor.get_color(obj["type"])))

size = int(size * pixels_per_meter)
mask = np.zeros([size, size, 1], dtype=np.float32)
mask[..., 0] = color_setting.get_color(MetaDriveType.GROUND)
# create an example bounding box polygon
# for idx in range(len(polygons)):
for polygon, color in polygons:
)
else:
polylines.append((obj["polyline"], MapTerrainSemanticColor.get_color(obj["type"])))

size = int(size * pixels_per_meter)
mask = np.zeros([size, size, 1], dtype=np.float32)
mask[..., 0] = color_setting.get_color(MetaDriveType.GROUND)
# create an example bounding box polygon
# for idx in range(len(polygons)):
for polygon, color in polygons:
points = [
[
int((x - center_p[0]) * pixels_per_meter + size / 2),
int((y - center_p[1]) * pixels_per_meter) + size / 2
] for x, y in polygon
]
cv2.fillPoly(mask, np.array([points]).astype(np.int32), color=color)
for line, color in polylines:
points = [
[
int((p[0] - center_p[0]) * pixels_per_meter + size / 2),
int((p[1] - center_p[1]) * pixels_per_meter) + size / 2
] for p in line
]
thickness = polyline_thickness * 2 if color == MapTerrainSemanticColor.YELLOW else polyline_thickness
thickness = min(thickness, 2) # clip
cv2.polylines(mask, np.array([points]).astype(np.int32), False, color, thickness)

if "crosswalk" in layer:
for id, sidewalk in self.crosswalks.items():
polygon = sidewalk["polygon"]
points = [
[
int((x - center_p[0]) * pixels_per_meter + size / 2),
int((y - center_p[1]) * pixels_per_meter) + size / 2
] for x, y in polygon
]
cv2.fillPoly(mask, np.array([points]).astype(np.int32), color=color)
for line, color in polylines:
points = [
[
int((p[0] - center_p[0]) * pixels_per_meter + size / 2),
int((p[1] - center_p[1]) * pixels_per_meter) + size / 2
] for p in line
]
thickness = polyline_thickness * 2 if color == MapTerrainSemanticColor.YELLOW else polyline_thickness
thickness = min(thickness, 2) # clip
cv2.polylines(mask, np.array([points]).astype(np.int32), False, color, thickness)

if "crosswalk" in layer:
for id, sidewalk in self.crosswalks.items():
polygon = sidewalk["polygon"]
points = [
[
int((x - center_p[0]) * pixels_per_meter + size / 2),
int((y - center_p[1]) * pixels_per_meter) + size / 2
] for x, y in polygon
]
# edges = find_longest_parallel_edges(polygon)
# p_1, p_2 = edges[0]
p_1, p_2 = find_longest_edge(polygon)[0]
dir = (
p_2[0] - p_1[0],
p_2[1] - p_1[1],
)
# 0-2pi
angle = np.arctan2(*dir) / np.pi * 180 + 180
# normalize to 0.4-0.714
angle = angle / 1000 + MapTerrainSemanticColor.get_color(MetaDriveType.CROSSWALK)
cv2.fillPoly(mask, np.array([points]).astype(np.int32), color=angle)

self._semantic_map = mask
return self._semantic_map
# edges = find_longest_parallel_edges(polygon)
# p_1, p_2 = edges[0]
p_1, p_2 = find_longest_edge(polygon)[0]
dir = (
p_2[0] - p_1[0],
p_2[1] - p_1[1],
)
# 0-2pi
angle = np.arctan2(*dir) / np.pi * 180 + 180
# normalize to 0.4-0.714
angle = angle / 1000 + MapTerrainSemanticColor.get_color(MetaDriveType.CROSSWALK)
cv2.fillPoly(mask, np.array([points]).astype(np.int32), color=angle)

# self._semantic_map = mask
# return self._semantic_map
return mask

# @time_me
def get_height_map(
Expand All @@ -294,40 +296,42 @@ def get_height_map(
:return: heightfield image in uint 16 nparray
"""
center_p = center_point
if self._height_map is None:
extension = max(1, extension)
all_lanes = self.get_map_features()
polygons = []

for obj in all_lanes.values():
if MetaDriveType.is_lane(obj["type"]):
polygons.append(obj["polygon"])

size = int(size * pixels_per_meter)
mask = np.zeros([size, size, 1])

need_scale = abs(extension - 1) > 1e-1

for sidewalk in self.sidewalks.values():
polygons.append(sidewalk["polygon"])

for polygon in polygons:
points = [
[
int((x - center_p[0]) * pixels_per_meter + size / 2),
int((y - center_p[1]) * pixels_per_meter) + size / 2
] for x, y in polygon
]
cv2.fillPoly(mask, np.asarray([points]).astype(np.int32), color=[height])
if need_scale:
# Define a kernel. A 3x3 rectangle kernel
kernel = np.ones(((extension + 1) * pixels_per_meter, (extension + 1) * pixels_per_meter), np.uint8)

# Apply dilation
mask = cv2.dilate(mask, kernel, iterations=1)
mask = np.expand_dims(mask, axis=-1)
self._height_map = mask
return self._height_map
# if self._height_map is None:
extension = max(1, extension)
all_lanes = self.get_map_features()
polygons = []

for obj in all_lanes.values():
if MetaDriveType.is_lane(obj["type"]):
polygons.append(obj["polygon"])

size = int(size * pixels_per_meter)
mask = np.zeros([size, size, 1])

need_scale = abs(extension - 1) > 1e-1

for sidewalk in self.sidewalks.values():
polygons.append(sidewalk["polygon"])

for polygon in polygons:
points = [
[
int((x - center_p[0]) * pixels_per_meter + size / 2),
int((y - center_p[1]) * pixels_per_meter) + size / 2
] for x, y in polygon
]
cv2.fillPoly(mask, np.asarray([points]).astype(np.int32), color=[height])
if need_scale:
# Define a kernel. A 3x3 rectangle kernel
kernel = np.ones(((extension + 1) * pixels_per_meter, (extension + 1) * pixels_per_meter), np.uint8)

# Apply dilation
mask = cv2.dilate(mask, kernel, iterations=1)
mask = np.expand_dims(mask, axis=-1)
# self._height_map = mask
# return self._height_map
return mask

def show_bounding_box(self):
"""
Expand Down
8 changes: 6 additions & 2 deletions metadrive/engine/core/terrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,11 @@ def reset(self, center_point):

if self.render:
# Make semantics for shader terrain
self.semantic_tex.setRamImage(self.get_terrain_semantics(center_point))
# Makre height field
self.semantic_tex.clearRamImage()
rrr = self.get_terrain_semantics(center_point)
self.semantic_tex.setRamImage(rrr)
# Make height field
self.heightfield_tex.clearRamImage()
self.heightfield_tex.setRamImage(heightfield_base)
# generate terrain visualization
self._generate_mesh_vis_terrain(self._terrain_size, self.heightfield_tex, self.semantic_tex)
Expand Down Expand Up @@ -328,6 +331,7 @@ def _generate_collision_mesh(self, heightfield_img, height_scale):
self.dynamic_nodes.append(node)

self.mesh_collision_terrain = self.origin.attachNewNode(node)
self._node_path_list.append(self.mesh_collision_terrain)

def set_position(self, position, height=None):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
We find running rendering (windows or RGB camera) with PG env with multiple scenarios has severe memory leak.
"""
from metadrive.component.sensors.rgb_camera import RGBCamera
from metadrive.envs.metadrive_env import MetaDriveEnv


def test_safe_env_memory_leak():
# Initialize environment
train_env_config = dict(
# manual_control=False, # Allow receiving control signal from external device
# window_size=(200, 200),
horizon=1500,
# use_render=vis,
image_observation=True,
sensors=dict(rgb_camera=(RGBCamera, 256, 256)),
num_scenarios=100,
)

env = MetaDriveEnv(train_env_config)
try:
total_cost = 0
for ep in range(20):
o, _ = env.reset()
env.engine.force_fps.disable()
for i in range(1, 1000):
o, r, tm, tc, info = env.step([0, 1])
total_cost += info["cost"]
assert env.observation_space.contains(o)
if tm or tc:
total_cost = 0
break
finally:
env.close()


if __name__ == '__main__':
test_safe_env_memory_leak()

0 comments on commit 6504392

Please sign in to comment.