Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unload render engine when the sensors system exits #1960

Merged
merged 10 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/gz/sim/rendering/MarkerManager.hh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ class GZ_SIM_RENDERING_VISIBLE MarkerManager
/// \param[in] _name Name of service
public: void SetTopic(const std::string &_name);

/// \brief Clear the marker manager
/// Clears internal resources stored in the marker manager.
/// Note: this does not actually destroy the objects.
public: void Clear();

/// \internal
/// \brief Private data pointer
private: std::unique_ptr<MarkerManagerPrivate> dataPtr;
Expand Down
3 changes: 3 additions & 0 deletions include/gz/sim/rendering/RenderUtil.hh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ inline namespace GZ_SIM_VERSION_NAMESPACE {
/// \brief Initialize the renderer. Must be called in the rendering thread.
public: void Init();

/// \brief Destroy the renderer. Must be called in the rendering thread.
public: void Destroy();

/// \brief Count of pending sensors. Must be called in the rendering thread.
/// \return Number of sensors to be added on the next `Update` call
///
Expand Down
5 changes: 5 additions & 0 deletions include/gz/sim/rendering/SceneManager.hh
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ inline namespace GZ_SIM_VERSION_NAMESPACE {
/// IDs are available
public: Entity UniqueId() const;

/// \brief Clear the scene manager
/// Clears internal resources stored in the scene manager.
/// Note: this does not actually destroy the objects.
public: void Clear();

/// \internal
/// \brief Pointer to private data class
private: std::unique_ptr<SceneManagerPrivate> dataPtr;
Expand Down
8 changes: 8 additions & 0 deletions src/rendering/MarkerManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,11 @@ bool MarkerManagerPrivate::OnMarkerMsgArray(
_res.set_data(true);
return true;
}

/////////////////////////////////////////////////
void MarkerManager::Clear()
{
this->dataPtr->visuals.clear();
this->dataPtr->markerMsgs.clear();
this->dataPtr->scene.reset();
}
15 changes: 15 additions & 0 deletions src/rendering/RenderUtil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,21 @@ void RenderUtil::Init()
this->dataPtr->markerManager.Init(this->dataPtr->scene);
}

/////////////////////////////////////////////////
void RenderUtil::Destroy()
{
if (!this->dataPtr->engine || !this->dataPtr->scene)
return;
this->dataPtr->wireBoxes.clear();
this->dataPtr->sceneManager.Clear();
this->dataPtr->markerManager.Clear();
this->dataPtr->engine->DestroyScene(this->dataPtr->scene);
this->dataPtr->scene.reset();
rendering::unloadEngine(this->dataPtr->engine->Name());
this->dataPtr->engine = nullptr;
this->dataPtr->initialized = false;
}

/////////////////////////////////////////////////
void RenderUtil::SetBackgroundColor(const math::Color &_color)
{
Expand Down
15 changes: 15 additions & 0 deletions src/rendering/SceneManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2457,3 +2457,18 @@ SceneManagerPrivate::LoadTrajectories(const sdf::Actor &_actor,
}
return trajectories;
}

/////////////////////////////////////////////////
void SceneManager::Clear()
{
this->dataPtr->visuals.clear();
this->dataPtr->actors.clear();
this->dataPtr->actorSkeletons.clear();
this->dataPtr->actorTrajectories.clear();
this->dataPtr->lights.clear();
this->dataPtr->particleEmitters.clear();
this->dataPtr->sensors.clear();
this->dataPtr->scene.reset();
this->dataPtr->originalTransparency.clear();
this->dataPtr->originalDepthWrite.clear();
}
2 changes: 2 additions & 0 deletions src/systems/sensors/Sensors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ void SensorsPrivate::WaitForInit()
if (this->ambientLight)
this->renderUtil.SetAmbientLight(*this->ambientLight);
#ifndef __APPLE__

this->renderUtil.Init();
#else
// On macOS the render engine must be initialised on the main thread.
Expand Down Expand Up @@ -409,6 +410,7 @@ void SensorsPrivate::RenderThread()
for (const auto id : this->sensorIds)
this->sensorManager.Remove(id);

this->renderUtil.Destroy();
gzdbg << "SensorsPrivate::RenderThread stopped" << std::endl;
}

Expand Down
2 changes: 2 additions & 0 deletions test/integration/ModelPhotoShootTest.hh
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ class ModelPhotoShootTest : public InternalFixture<::testing::Test>
testImages("3.png", "3_test.png");
testImages("4.png", "4_test.png");
testImages("5.png", "5_test.png");

postRenderConn.reset();
}

private: bool takeTestPics{false};
Expand Down
21 changes: 21 additions & 0 deletions test/performance/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ set(tests
level_manager.cc
)

set(tests_needing_display
sensors_system.cc
)

# Add systems that need a valid display here.
if(VALID_DISPLAY AND VALID_DRI_DISPLAY)
list(APPEND tests ${tests_needing_display})
else()
message(STATUS
"Skipping these PERFORMANCE tests because a valid display was not found:")
foreach(test ${tests_needing_display})
message(STATUS " ${test}")
endforeach(test)
endif()

set(exec
sdf_runner
)
Expand Down Expand Up @@ -39,3 +54,9 @@ target_link_libraries(
gz-sim${PROJECT_VERSION_MAJOR}
gz-sim${PROJECT_VERSION_MAJOR}-gui
)

if(VALID_DISPLAY AND VALID_DRI_DISPLAY AND TARGET PERFORMANCE_sensors_system)
target_link_libraries(PERFORMANCE_sensors_system
gz-rendering${GZ_RENDERING_VER}::gz-rendering${GZ_RENDERING_VER}
)
endif()
136 changes: 136 additions & 0 deletions test/performance/sensors_system.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2023 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

// The following is needed to enable the GetMemInfo function for OSX
#ifdef __MACH__
# include <mach/mach.h>
#endif // __MACH__

#include <gtest/gtest.h>

#include <gz/common/Console.hh>
#include <gz/rendering/RenderingIface.hh>

#include <gz/utils/ExtraTestMacros.hh>

#include "gz/sim/Server.hh"
#include "test_config.hh"

#include "../helpers/EnvTestFixture.hh"

/////////////////////////////////////////////////
void getMemInfo(double &_resident, double &_share)
{
#ifdef __linux__
int totalSize, residentPages, sharePages;
totalSize = residentPages = sharePages = 0;

std::ifstream buffer("/proc/self/statm");
buffer >> totalSize >> residentPages >> sharePages;
buffer.close();

// in case x86-64 is configured to use 2MB pages
int64_t pageSizeKb = sysconf(_SC_PAGE_SIZE) / 1024;

_resident = residentPages * pageSizeKb;
_share = sharePages * pageSizeKb;
#elif __MACH__
// /proc is only available on Linux
// for OSX, use task_info to get resident and virtual memory
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if (KERN_SUCCESS != task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&t_info,
&t_info_count))
{
gzerr << "failure calling task_info\n";
return;
}
_resident = static_cast<double>(t_info.resident_size/1024);
_share = static_cast<double>(t_info.virtual_size/1024);
#else
gzerr << "Unsupported architecture\n";
return;
#endif
}

/////////////////////////////////////////////////
class SensorsSystemFixture : public InternalFixture<::testing::Test>
{
};


/////////////////////////////////////////////////
// Test repeatedly launching sim with sensors system and check for
// memory leaks
TEST_F(SensorsSystemFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(MemLeak))
{
gz::common::Console::SetVerbosity(4);

// max memory change allowed
const double resMaxPercentChange = 1.0;
const double shareMaxPercentChange = 1.0;

std::vector<double> residentMemV;
std::vector<double> shareMemV;

for (unsigned int i = 0; i < 5; ++i)
{
// get resident and shared memory usage
double residentMem = 0;
double shareMem = 0;
getMemInfo(residentMem, shareMem);

residentMemV.push_back(residentMem);
shareMemV.push_back(shareMem);

gz::sim::ServerConfig serverConfig;

const std::string sdfFile = std::string(PROJECT_SOURCE_PATH) +
"/test/worlds/sensors_system_mem_leak.sdf";

serverConfig.SetSdfFile(sdfFile);

gz::sim::Server server(serverConfig);
server.Run(true, 100, false);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}

// Calculate the percent change between runs
// Start from index 1 (instead of 0) since there is a big jump in memory usage
// when the system is first loaded. Make sure subsequent runs do not continue
// to increase memory usage
for (unsigned int i = 1; i < residentMemV.size() - 1; ++i)
{
double resPercentChange =
(residentMemV[i+1] - residentMemV[i]) / residentMemV[i];
double sharePercentChange = (shareMemV[i+1] - shareMemV[i]) / shareMemV[i];

gzdbg << "ResPercentChange[" << resPercentChange << "] "
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
<< "ResMaxPercentChange[" << resMaxPercentChange << "]" << std::endl;
gzdbg << "SharePercentChange[" << sharePercentChange << "] "
<< "ShareMaxPercentChange[" << shareMaxPercentChange << "]" << std::endl;

// check for memory leaks
// \todo(anyone) there is still gradual slow increase in memory usage
// between runs, likely from the sensors system / gz-rendering.
// Use tighter max percentage change once more mem leaks are fixed
EXPECT_LT(resPercentChange, resMaxPercentChange);
EXPECT_LT(sharePercentChange, shareMaxPercentChange);
}
}
2 changes: 1 addition & 1 deletion test/worlds/lights_render.sdf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<plugin
filename="gz-sim-sensors-system"
name="gz::sim::systems::Sensors">
<render_engine>ogre</render_engine>
<render_engine>ogre2</render_engine>
</plugin>
<light type="directional" name="directional">
<cast_shadows>true</cast_shadows>
Expand Down
69 changes: 69 additions & 0 deletions test/worlds/sensors_system_mem_leak.sdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="sensors_system">

<plugin
filename="gz-sim-sensors-system"
name="gz::sim::systems::Sensors">
<render_engine>ogre2</render_engine>
</plugin>

<model name="box">
<pose>0 0 0 0 0 0</pose>
<static>true</static>
<link name="link">
<collision name="collision_0">
<geometry>
<box><size>5 5 2.5</size></box>
</geometry>
</collision>
<visual name="visual_0">
<geometry>
<box><size>5 5 2.5</size></box>
</geometry>
<material>
<ambient>0 1 0 0.8</ambient>
<diffuse>0 1 0 0.8</diffuse>
<specular>1 1 1 0.8</specular>
</material>
</visual>
</link>
</model>

<model name="camera">
<static>true</static>
<pose>-6 2 2 0 0.5 0</pose>
<link name="link">
<visual name="visual">
<geometry>
<box>
<size>0.1 0.1 0.1</size>
</box>
</geometry>
</visual>
<sensor name="camera" type="camera">
<pose>1 0 1.3 0 0 0</pose>
<camera>
<horizontal_fov>1.047</horizontal_fov>
<image>
<width>320</width>
<height>240</height>
</image>
<clip>
<near>0.1</near>
<far>100</far>
</clip>
<noise>
<type>gaussian_quantized</type>
<mean>0.01</mean>
<stddev>0.0002</stddev>
</noise>
</camera>
<always_on>1</always_on>
<update_rate>30</update_rate>
<topic>camera</topic>
</sensor>
</link>
</model>
</world>
</sdf>