1111
1212namespace UI
1313{
14- /* *
15- * FIXME: #87 This class is not thread safe since m_heightmap can be accessed and set by multiple threads without
16- * synchronization. This can cause the program to crash if for example the Duet disconnects while a heightmap is
17- * being rendered.
18- */
19-
2014 void HeightmapPresenter::setRenderMode (HeightmapRenderMode mode)
2115 {
2216 ZoneScoped;
@@ -27,8 +21,11 @@ namespace UI
2721 void HeightmapPresenter::setHeightmap (const std::shared_ptr<OM::Heightmap>& heightmap)
2822 {
2923 ZoneScoped;
30- m_heightmap = heightmap;
31- if (m_heightmap == nullptr )
24+ {
25+ std::unique_lock lock (m_heightmapMutex);
26+ m_heightmap = heightmap;
27+ }
28+ if (heightmap == nullptr )
3229 {
3330 m_view->clear ();
3431 }
@@ -42,31 +39,37 @@ namespace UI
4239 return ;
4340 }
4441
45- if (m_heightmap == nullptr )
42+ std::shared_ptr<OM::Heightmap> heightmap;
43+ {
44+ std::shared_lock lock (m_heightmapMutex);
45+ heightmap = m_heightmap;
46+ }
47+
48+ if (heightmap == nullptr )
4649 {
4750 m_view->clear ();
4851 return ;
4952 }
5053
51- if (!m_heightmap ->IsValid ())
54+ if (!heightmap ->IsValid ())
5255 {
5356 LOG_WARN (" Heightmap is not valid" );
5457 m_view->clear ();
5558 return ;
5659 }
5760
58- auto axis0 = m_heightmap ->meta .GetAxis (0 );
59- auto axis1 = m_heightmap ->meta .GetAxis (1 );
61+ auto axis0 = heightmap ->meta .GetAxis (0 );
62+ auto axis1 = heightmap ->meta .GetAxis (1 );
6063
6164 if (axis0 == nullptr || axis1 == nullptr )
6265 {
6366 LOG_WARN (" Heightmap axes are not valid" );
6467 return ;
6568 }
6669
67- LOG_DBG (" Rendering heightmap {:s}" , m_heightmap ->GetFileName ());
70+ LOG_DBG (" Rendering heightmap {:s}" , heightmap ->GetFileName ());
6871
69- m_view->setShownHeightmapName (m_heightmap ->GetFileName ());
72+ m_view->setShownHeightmapName (heightmap ->GetFileName ());
7073 m_view->setXRange ({static_cast <int32_t >(axis0->minPosition ), static_cast <int32_t >(axis0->maxPosition )});
7174 m_view->setYRange ({static_cast <int32_t >(axis1->minPosition ), static_cast <int32_t >(axis1->maxPosition )});
7275
@@ -79,8 +82,8 @@ namespace UI
7982 m_view->setValueRange (-0 .25f , 0 .25f );
8083 break ;
8184 case HeightmapRenderMode::Auto:
82- m_view->setValueRange (static_cast <float >(m_heightmap ->GetMinError ()),
83- static_cast <float >(m_heightmap ->GetMaxError ()));
85+ m_view->setValueRange (static_cast <float >(heightmap ->GetMinError ()),
86+ static_cast <float >(heightmap ->GetMaxError ()));
8487 break ;
8588 }
8689
@@ -113,7 +116,7 @@ namespace UI
113116 {
114117 float x = x_min + (static_cast <float >(px) * xStep);
115118 float y = y_min + (static_cast <float >(py) * yStep);
116- double value = m_heightmap ->GetInterpolatedPoint (x, y);
119+ double value = heightmap ->GetInterpolatedPoint (x, y);
117120 if (std::isnan (value))
118121 {
119122 continue ;
@@ -126,20 +129,20 @@ namespace UI
126129 m_view->renderColorBar ();
127130
128131#if RENDER_MEASUREMENT_POINTS
129- const auto & measurements = m_heightmap ->GetPoints ();
132+ const auto & measurements = heightmap ->GetPoints ();
130133 for (size_t i = 0 ; i < measurements.size (); i++)
131134 {
132135 const auto & point = measurements[i];
133136 m_view->addMeasurementPoint (point.x , point.y );
134137 }
135138#endif
136139
137- m_view->setStatistics (m_heightmap ->GetPointCount (),
138- m_heightmap ->GetArea () / 100 ,
139- m_heightmap ->GetMinError (),
140- m_heightmap ->GetMaxError (),
141- m_heightmap ->GetMeanError (),
142- m_heightmap ->GetStdDev ());
140+ m_view->setStatistics (heightmap ->GetPointCount (),
141+ heightmap ->GetArea () / 100 ,
142+ heightmap ->GetMinError (),
143+ heightmap ->GetMaxError (),
144+ heightmap ->GetMeanError (),
145+ heightmap ->GetStdDev ());
143146 }
144147
145148 void HeightmapPresenter::setActiveHeightmap (const size_t index)
@@ -153,8 +156,15 @@ namespace UI
153156
154157 const std::string& name = m_heightmapFiles[index]->GetName ();
155158 LOG_INFO (" Loading heightmap {:s}" , name);
156- m_heightmap = OM::GetHeightmapData (name);
157- m_heightmap->LoadFromDuet ([this ](OM::Heightmap& /* heightmap */ ) { render (); });
159+ auto map = OM::GetHeightmapData (name);
160+ {
161+ std::unique_lock lock (m_heightmapMutex);
162+ m_heightmap = map;
163+ }
164+ if (map)
165+ {
166+ map->LoadFromDuet ([this ](OM::Heightmap& /* heightmap */ ) { render (); });
167+ }
158168 }
159169
160170 void HeightmapPresenter::toggleHeightmap (const size_t index)
@@ -212,14 +222,19 @@ namespace UI
212222 {
213223 ZoneScoped;
214224 LOG_DBG (" New axes data" );
215- if (m_heightmap == nullptr )
225+ std::shared_ptr<OM::Heightmap> heightmap;
226+ {
227+ std::shared_lock lock (m_heightmapMutex);
228+ heightmap = m_heightmap;
229+ }
230+ if (heightmap == nullptr )
216231 {
217232 LOG_DBG (" Heightmap is not set, skipping axis range update" );
218233 return ;
219234 }
220235
221- auto axis0 = m_heightmap ->meta .GetAxis (0 );
222- auto axis1 = m_heightmap ->meta .GetAxis (1 );
236+ auto axis0 = heightmap ->meta .GetAxis (0 );
237+ auto axis1 = heightmap ->meta .GetAxis (1 );
223238
224239 if (axis0 == nullptr || axis1 == nullptr )
225240 {
@@ -234,7 +249,7 @@ namespace UI
234249 return ;
235250 }
236251
237- if (m_heightmap != nullptr && m_heightmap ->IsValid ())
252+ if (heightmap != nullptr && heightmap ->IsValid ())
238253 {
239254 render ();
240255 }
@@ -323,11 +338,14 @@ namespace UI
323338 {
324339 ZoneScoped;
325340 LOG_DBG (" Disconnect" );
326- if (m_heightmap != nullptr )
327341 {
328- m_heightmap = nullptr ;
329- m_view->clear ();
330- m_view->setHeightmapCount (0 );
342+ std::unique_lock lock (m_heightmapMutex);
343+ if (m_heightmap != nullptr )
344+ {
345+ m_heightmap = nullptr ;
346+ }
331347 }
348+ m_view->clear ();
349+ m_view->setHeightmapCount (0 );
332350 }
333351} // namespace UI
0 commit comments