Skip to content

Commit fc02f5b

Browse files
95x8x9learncold
authored andcommitted
[Domain] reduce simulation and replay overhead
1 parent bc4ec2e commit fc02f5b

12 files changed

Lines changed: 675 additions & 83 deletions

src/application/ScenarioBatchResultWidget.cpp

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -990,28 +990,12 @@ void ScenarioBatchResultWidget::applyReplayFrameData(const safecrowd::domain::Si
990990
if (results_.empty() || currentResultIndex_ < 0 || currentResultIndex_ >= static_cast<int>(results_.size())) {
991991
return;
992992
}
993-
const auto& result = results_[static_cast<std::size_t>(currentResultIndex_)];
994993
replayFrameIndex_ = replayFrames_.empty()
995994
? 0
996995
: std::clamp(sliderIndex, 0, static_cast<int>(replayFrames_.size()) - 1);
997996

998997
if (canvas_ != nullptr) {
999-
canvas_->setConnectionBlocks(result.scenario.control.connectionBlocks);
1000-
canvas_->setEnvironmentHazards(result.scenario.environment.hazards);
1001998
canvas_->setFrame(frame);
1002-
canvas_->setHotspotOverlay(result.risk.hotspots);
1003-
canvas_->setBottleneckOverlay(result.risk.bottlenecks);
1004-
canvas_->setDensityOverlay(result.artifacts.densitySummary.peakField.cells.empty()
1005-
? result.artifacts.densitySummary.peakCells
1006-
: result.artifacts.densitySummary.peakField.cells,
1007-
result.artifacts.densitySummary.highDensityThresholdPeoplePerSquareMeter);
1008-
canvas_->setPressureOverlay(result.artifacts.pressureSummary.peakField.cells.empty()
1009-
? result.artifacts.pressureSummary.peakCells
1010-
: result.artifacts.pressureSummary.peakField.cells,
1011-
std::max(
1012-
result.artifacts.pressureSummary.hotspotScoreThreshold,
1013-
result.artifacts.pressureSummary.peakPressureScore));
1014-
applyOverlayModeToCanvas();
1015999
}
10161000
if (replaySlider_ != nullptr && replaySlider_->value() != replayFrameIndex_) {
10171001
const QSignalBlocker blocker(replaySlider_);
@@ -1031,6 +1015,34 @@ void ScenarioBatchResultWidget::applyReplayFrameData(const safecrowd::domain::Si
10311015
}
10321016
}
10331017

1018+
void ScenarioBatchResultWidget::applySelectedResultStaticCanvasState() {
1019+
if (canvas_ == nullptr
1020+
|| results_.empty()
1021+
|| currentResultIndex_ < 0
1022+
|| currentResultIndex_ >= static_cast<int>(results_.size())) {
1023+
return;
1024+
}
1025+
1026+
const auto& result = results_[static_cast<std::size_t>(currentResultIndex_)];
1027+
canvas_->setConnectionBlocks(result.scenario.control.connectionBlocks);
1028+
canvas_->setEnvironmentHazards(result.scenario.environment.hazards);
1029+
canvas_->setHotspotOverlay(result.risk.hotspots);
1030+
canvas_->setBottleneckOverlay(result.risk.bottlenecks);
1031+
canvas_->setDensityOverlay(
1032+
result.artifacts.densitySummary.peakField.cells.empty()
1033+
? result.artifacts.densitySummary.peakCells
1034+
: result.artifacts.densitySummary.peakField.cells,
1035+
result.artifacts.densitySummary.highDensityThresholdPeoplePerSquareMeter);
1036+
canvas_->setPressureOverlay(
1037+
result.artifacts.pressureSummary.peakField.cells.empty()
1038+
? result.artifacts.pressureSummary.peakCells
1039+
: result.artifacts.pressureSummary.peakField.cells,
1040+
std::max(
1041+
result.artifacts.pressureSummary.hotspotScoreThreshold,
1042+
result.artifacts.pressureSummary.peakPressureScore));
1043+
applyOverlayModeToCanvas();
1044+
}
1045+
10341046
void ScenarioBatchResultWidget::applyOverlayModeToCanvas() {
10351047
if (canvas_ == nullptr) {
10361048
return;
@@ -1125,6 +1137,7 @@ void ScenarioBatchResultWidget::loadReplayForSelectedResult() {
11251137
replayFrames_.clear();
11261138
return;
11271139
}
1140+
applySelectedResultStaticCanvasState();
11281141
replayFrames_ = replayFramesForResult(results_[static_cast<std::size_t>(currentResultIndex_)]);
11291142
replayFrameIndex_ = replayFrames_.empty() ? 0 : static_cast<int>(replayFrames_.size()) - 1;
11301143
if (replaySlider_ != nullptr) {

src/application/ScenarioBatchResultWidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class ScenarioBatchResultWidget : public QWidget {
5959
void applyReplayFrame(int frameIndex);
6060
void applyReplayFrameData(const safecrowd::domain::SimulationFrame& frame, int sliderIndex);
6161
void applyOverlayModeToCanvas();
62+
void applySelectedResultStaticCanvasState();
6263
void loadReplayForSelectedResult();
6364
int nearestReplayFrameIndex(double seconds) const;
6465
void navigateToAuthoring();

src/application/SimulationCanvasWidget.cpp

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ void SimulationCanvasWidget::setDensityOverlay(
544544
std::isfinite(scaleMaxPeoplePerSquareMeter) && scaleMaxPeoplePerSquareMeter > 0.0
545545
? scaleMaxPeoplePerSquareMeter
546546
: kDefaultDensityScaleMaxPeoplePerSquareMeter;
547+
invalidateOverlayCache();
547548
update();
548549
}
549550

@@ -555,6 +556,7 @@ void SimulationCanvasWidget::setPressureOverlay(
555556
std::isfinite(scaleMaxPressureScore) && scaleMaxPressureScore > 0.0
556557
? scaleMaxPressureScore
557558
: kDefaultPressureScaleMaxScore;
559+
invalidateOverlayCache();
558560
update();
559561
}
560562

@@ -563,6 +565,7 @@ void SimulationCanvasWidget::setHotspotOverlay(std::vector<safecrowd::domain::Sc
563565
if (focusedHotspotIndex_.has_value() && *focusedHotspotIndex_ >= hotspotOverlay_.size()) {
564566
focusedHotspotIndex_.reset();
565567
}
568+
invalidateOverlayCache();
566569
update();
567570
}
568571

@@ -571,11 +574,16 @@ void SimulationCanvasWidget::setBottleneckOverlay(std::vector<safecrowd::domain:
571574
if (focusedBottleneckIndex_.has_value() && *focusedBottleneckIndex_ >= bottleneckOverlay_.size()) {
572575
focusedBottleneckIndex_.reset();
573576
}
577+
invalidateOverlayCache();
574578
update();
575579
}
576580

577581
void SimulationCanvasWidget::setResultOverlayMode(ResultOverlayMode mode) {
582+
if (overlayMode_ == mode) {
583+
return;
584+
}
578585
overlayMode_ = mode;
586+
invalidateOverlayCache();
579587
update();
580588
}
581589

@@ -589,6 +597,7 @@ void SimulationCanvasWidget::focusHotspot(std::size_t index) {
589597
}
590598
focusedHotspotIndex_ = index;
591599
focusedBottleneckIndex_.reset();
600+
invalidateOverlayCache();
592601
focusWorldPoint(hotspotOverlay_[index].center, std::max(camera_.zoom(), kHotspotFocusZoom));
593602
}
594603

@@ -603,6 +612,7 @@ void SimulationCanvasWidget::focusBottleneck(std::size_t index) {
603612
}
604613
focusedBottleneckIndex_ = index;
605614
focusedHotspotIndex_.reset();
615+
invalidateOverlayCache();
606616
focusWorldPoint(
607617
{.x = (passage.start.x + passage.end.x) / 2.0, .y = (passage.start.y + passage.end.y) / 2.0},
608618
std::max(camera_.zoom(), kBottleneckFocusZoom));
@@ -640,6 +650,7 @@ void SimulationCanvasWidget::mouseDoubleClickEvent(QMouseEvent* event) {
640650
if (event->button() == Qt::LeftButton) {
641651
camera_.reset();
642652
layoutCacheValid_ = false;
653+
invalidateOverlayCache();
643654
update();
644655
event->accept();
645656
return;
@@ -658,6 +669,7 @@ void SimulationCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
658669
QToolTip::hideText();
659670
}
660671
layoutCacheValid_ = false;
672+
invalidateOverlayCache();
661673
update();
662674
return;
663675
}
@@ -807,14 +819,9 @@ void SimulationCanvasWidget::paintEvent(QPaintEvent* event) {
807819
painter.drawPixmap(0, 0, layoutCache_);
808820

809821
const auto transform = currentTransform(*bounds);
810-
if (overlayMode_ == ResultOverlayMode::Density) {
811-
drawDensityOverlay(painter, transform);
812-
} else if (overlayMode_ == ResultOverlayMode::Pressure) {
813-
drawPressureOverlay(painter, transform);
814-
} else if (overlayMode_ == ResultOverlayMode::Hotspots) {
815-
drawHotspotOverlay(painter, transform);
816-
} else if (overlayMode_ == ResultOverlayMode::Bottlenecks) {
817-
drawBottleneckOverlay(painter, transform);
822+
refreshOverlayCache(*bounds);
823+
if (!overlayCache_.isNull()) {
824+
painter.drawPixmap(0, 0, overlayCache_);
818825
}
819826
drawEnvironmentHazardOverlay(painter, transform);
820827
drawConnectionBlockOverlay(painter, transform);
@@ -847,6 +854,7 @@ void SimulationCanvasWidget::wheelEvent(QWheelEvent* event) {
847854
}
848855
if (camera_.zoomAt(event, *bounds, previewViewport())) {
849856
layoutCacheValid_ = false;
857+
invalidateOverlayCache();
850858
update();
851859
return;
852860
}
@@ -901,6 +909,69 @@ void SimulationCanvasWidget::refreshLayoutCache(const LayoutCanvasBounds& bounds
901909
layoutCacheValid_ = true;
902910
}
903911

912+
void SimulationCanvasWidget::refreshOverlayCache(const LayoutCanvasBounds& bounds) {
913+
if (overlayMode_ == ResultOverlayMode::None) {
914+
overlayCache_ = QPixmap();
915+
overlayCacheValid_ = true;
916+
overlayCacheMode_ = overlayMode_;
917+
overlayCacheFloorId_ = currentFloorId_;
918+
return;
919+
}
920+
921+
const auto currentSize = size();
922+
if (currentSize.isEmpty()) {
923+
overlayCache_ = QPixmap();
924+
overlayCacheSize_ = currentSize;
925+
overlayCacheDevicePixelRatio_ = 0.0;
926+
overlayCacheValid_ = false;
927+
return;
928+
}
929+
930+
const auto devicePixelRatio = devicePixelRatioF();
931+
if (overlayCacheValid_
932+
&& overlayCacheSize_ == currentSize
933+
&& overlayCacheDevicePixelRatio_ == devicePixelRatio
934+
&& overlayCacheZoom_ == camera_.zoom()
935+
&& overlayCachePan_ == camera_.panOffset()
936+
&& overlayCacheMode_ == overlayMode_
937+
&& overlayCacheFloorId_ == currentFloorId_) {
938+
return;
939+
}
940+
941+
const QSize physicalSize{
942+
std::max(1, static_cast<int>(std::ceil(currentSize.width() * devicePixelRatio))),
943+
std::max(1, static_cast<int>(std::ceil(currentSize.height() * devicePixelRatio))),
944+
};
945+
overlayCache_ = QPixmap(physicalSize);
946+
overlayCache_.setDevicePixelRatio(devicePixelRatio);
947+
overlayCache_.fill(Qt::transparent);
948+
949+
QPainter painter(&overlayCache_);
950+
painter.setRenderHint(QPainter::Antialiasing, true);
951+
const auto transform = currentTransform(bounds);
952+
if (overlayMode_ == ResultOverlayMode::Density) {
953+
drawDensityOverlay(painter, transform);
954+
} else if (overlayMode_ == ResultOverlayMode::Pressure) {
955+
drawPressureOverlay(painter, transform);
956+
} else if (overlayMode_ == ResultOverlayMode::Hotspots) {
957+
drawHotspotOverlay(painter, transform);
958+
} else if (overlayMode_ == ResultOverlayMode::Bottlenecks) {
959+
drawBottleneckOverlay(painter, transform);
960+
}
961+
962+
overlayCacheSize_ = currentSize;
963+
overlayCacheZoom_ = camera_.zoom();
964+
overlayCachePan_ = camera_.panOffset();
965+
overlayCacheDevicePixelRatio_ = devicePixelRatio;
966+
overlayCacheMode_ = overlayMode_;
967+
overlayCacheFloorId_ = currentFloorId_;
968+
overlayCacheValid_ = true;
969+
}
970+
971+
void SimulationCanvasWidget::invalidateOverlayCache() {
972+
overlayCacheValid_ = false;
973+
}
974+
904975
QRectF SimulationCanvasWidget::previewViewport() const {
905976
return QRectF(rect()).adjusted(kViewportPadding, kViewportPadding, -kViewportPadding, -kViewportPadding);
906977
}
@@ -922,6 +993,7 @@ void SimulationCanvasWidget::focusWorldPoint(const safecrowd::domain::Point2D& p
922993
const LayoutCanvasTransform transform(*bounds, viewport, camera_.zoom(), {});
923994
camera_.setPanOffset(viewport.center() - transform.map(point));
924995
layoutCacheValid_ = false;
996+
invalidateOverlayCache();
925997
update();
926998
}
927999

@@ -1404,6 +1476,7 @@ void SimulationCanvasWidget::setCurrentFloorId(std::string floorId, bool manualS
14041476
manualFloorSelection_ = manualSelection;
14051477
layoutBounds_ = collectLayoutCanvasBounds(layout_, currentFloorId_);
14061478
layoutCacheValid_ = false;
1479+
invalidateOverlayCache();
14071480
camera_.reset();
14081481

14091482
if (floorComboBox_ != nullptr) {

src/application/SimulationCanvasWidget.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class SimulationCanvasWidget : public QWidget {
7373
std::optional<LayoutCanvasBounds> collectBounds() const;
7474
LayoutCanvasTransform currentTransform(const LayoutCanvasBounds& bounds) const;
7575
void refreshLayoutCache(const LayoutCanvasBounds& bounds);
76+
void refreshOverlayCache(const LayoutCanvasBounds& bounds);
77+
void invalidateOverlayCache();
7678
QRectF previewViewport() const;
7779
void focusWorldPoint(const safecrowd::domain::Point2D& point, double zoom);
7880
void drawConnectionBlockOverlay(QPainter& painter, const LayoutCanvasTransform& transform) const;
@@ -113,6 +115,14 @@ class SimulationCanvasWidget : public QWidget {
113115
double layoutCacheZoom_{0.0};
114116
double layoutCacheDevicePixelRatio_{0.0};
115117
bool layoutCacheValid_{false};
118+
QPixmap overlayCache_{};
119+
QSize overlayCacheSize_{};
120+
QPointF overlayCachePan_{};
121+
double overlayCacheZoom_{0.0};
122+
double overlayCacheDevicePixelRatio_{0.0};
123+
ResultOverlayMode overlayCacheMode_{ResultOverlayMode::None};
124+
std::string overlayCacheFloorId_{};
125+
bool overlayCacheValid_{false};
116126

117127
std::string hoveredConnectionBlockId_{};
118128
std::string hoveredEnvironmentHazardId_{};

0 commit comments

Comments
 (0)