mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-08 04:31:08 -06:00
450 lines
16 KiB
C++
450 lines
16 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2017 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <modules/onscreengui/include/guiperformancecomponent.h>
|
|
|
|
#include <openspace/engine/openspaceengine.h>
|
|
#include <openspace/performance/performancelayout.h>
|
|
#include <openspace/performance/performancemanager.h>
|
|
#include <openspace/rendering/renderengine.h>
|
|
|
|
#include <ghoul/misc/sharedmemory.h>
|
|
|
|
#include <imgui.h>
|
|
#include <fmt/format.h>
|
|
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
|
|
namespace {
|
|
const std::string _loggerCat = "GuiPerformanceComponent";
|
|
|
|
enum class Sorting {
|
|
NoSorting = -1,
|
|
UpdateTranslation = 0,
|
|
UpdateRotation = 1,
|
|
UpdateScaling = 2,
|
|
UpdateRender = 3,
|
|
Render = 4,
|
|
Total = 5
|
|
};
|
|
}
|
|
|
|
namespace openspace {
|
|
namespace gui {
|
|
|
|
GuiPerformanceComponent::GuiPerformanceComponent()
|
|
: GuiComponent("PerformanceComponent")
|
|
, _sortingSelection("sortingSelection", "Sorting", -1, -1, 6)
|
|
, _sceneGraphIsEnabled("showSceneGraph", "Show Scene Graph Measurements", false)
|
|
, _functionsIsEnabled("showFunctions", "Show Function Measurements", false)
|
|
{
|
|
addProperty(_sortingSelection);
|
|
|
|
addProperty(_sceneGraphIsEnabled);
|
|
addProperty(_functionsIsEnabled);
|
|
}
|
|
|
|
void GuiPerformanceComponent::render() {
|
|
using ghoul::SharedMemory;
|
|
using namespace performance;
|
|
|
|
bool v = _isEnabled;
|
|
ImGui::Begin("Performance", &v);
|
|
_isEnabled = v;
|
|
PerformanceLayout* layout = OsEng.renderEngine().performanceManager()->performanceData();
|
|
|
|
v = _sceneGraphIsEnabled;
|
|
ImGui::Checkbox("SceneGraph", &v);
|
|
_sceneGraphIsEnabled = v;
|
|
v = _functionsIsEnabled;
|
|
ImGui::Checkbox("Functions", &v);
|
|
_functionsIsEnabled = v;
|
|
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::Button("Reset measurements")) {
|
|
OsEng.renderEngine().performanceManager()->resetPerformanceMeasurements();
|
|
}
|
|
|
|
if (_sceneGraphIsEnabled) {
|
|
bool v = _sceneGraphIsEnabled;
|
|
ImGui::Begin("SceneGraph", &v);
|
|
_sceneGraphIsEnabled = v;
|
|
|
|
// The indices correspond to the index into the average array further below
|
|
ImGui::Text("Sorting");
|
|
int sorting = _sortingSelection;
|
|
ImGui::RadioButton(
|
|
"No Sorting",
|
|
&sorting,
|
|
static_cast<int>(Sorting::NoSorting)
|
|
);
|
|
ImGui::RadioButton(
|
|
"UpdateTranslation",
|
|
&sorting,
|
|
static_cast<int>(Sorting::UpdateTranslation)
|
|
);
|
|
ImGui::RadioButton(
|
|
"UpdateRotation",
|
|
&sorting,
|
|
static_cast<int>(Sorting::UpdateRotation)
|
|
);
|
|
ImGui::RadioButton(
|
|
"UpdateScaling",
|
|
&sorting,
|
|
static_cast<int>(Sorting::UpdateScaling)
|
|
);
|
|
ImGui::RadioButton(
|
|
"UpdateRender",
|
|
&sorting,
|
|
static_cast<int>(Sorting::UpdateRender)
|
|
);
|
|
ImGui::RadioButton(
|
|
"RenderTime",
|
|
&sorting,
|
|
static_cast<int>(Sorting::Render)
|
|
);
|
|
ImGui::RadioButton(
|
|
"TotalTime",
|
|
&sorting,
|
|
static_cast<int>(Sorting::Total)
|
|
);
|
|
_sortingSelection = sorting;
|
|
|
|
// Later, we will sort this indices list instead of the real values for
|
|
// performance reasons
|
|
std::vector<size_t> indices(layout->nScaleGraphEntries);
|
|
std::iota(indices.begin(), indices.end(), 0);
|
|
|
|
// Ordering:
|
|
// updateTranslation
|
|
// updateRotation
|
|
// updateScaling
|
|
// UpdateRender
|
|
// RenderTime
|
|
std::vector<std::array<float, 5>> averages(
|
|
layout->nScaleGraphEntries,
|
|
{ 0.f, 0.f, 0.f, 0.f, 0.f }
|
|
);
|
|
|
|
std::vector<std::array<std::pair<float, float>, 5>> minMax(
|
|
layout->nScaleGraphEntries
|
|
);
|
|
|
|
for (int i = 0; i < layout->nScaleGraphEntries; ++i) {
|
|
const PerformanceLayout::SceneGraphPerformanceLayout& entry =
|
|
layout->sceneGraphEntries[i];
|
|
|
|
int nValues[5] = { 0, 0, 0, 0, 0 };
|
|
|
|
// Compute the averages and count the number of values so we don't divide
|
|
// by 0 later
|
|
for (int j = 0; j < PerformanceLayout::NumberValues; ++j) {
|
|
averages[i][0] += entry.updateTranslation[j];
|
|
if (entry.updateTranslation[j] != 0.f)
|
|
++(nValues[0]);
|
|
averages[i][1] += entry.updateRotation[j];
|
|
if (entry.updateRotation[j] != 0.f)
|
|
++(nValues[1]);
|
|
averages[i][2] += entry.updateScaling[j];
|
|
if (entry.updateScaling[j] != 0.f)
|
|
++(nValues[2]);
|
|
averages[i][3] += entry.updateRenderable[j];
|
|
if (entry.updateRenderable[j] != 0.f)
|
|
++(nValues[3]);
|
|
averages[i][4] += entry.renderTime[j];
|
|
if (entry.renderTime[j] != 0.f)
|
|
++(nValues[4]);
|
|
}
|
|
|
|
if (nValues[0] != 0) {
|
|
averages[i][0] /= static_cast<float>(nValues[0]);
|
|
}
|
|
if (nValues[1] != 0) {
|
|
averages[i][1] /= static_cast<float>(nValues[1]);
|
|
}
|
|
if (nValues[2] != 0) {
|
|
averages[i][2] /= static_cast<float>(nValues[2]);
|
|
}
|
|
if (nValues[3] != 0) {
|
|
averages[i][3] /= static_cast<float>(nValues[3]);
|
|
}
|
|
if (nValues[4] != 0) {
|
|
averages[i][4] /= static_cast<float>(nValues[4]);
|
|
}
|
|
|
|
// Get the minimum/maximum values for each of the components so that we
|
|
// can scale the plot by these numbers
|
|
auto minmaxTranslation = std::minmax_element(
|
|
std::begin(entry.updateTranslation),
|
|
std::end(entry.updateTranslation)
|
|
);
|
|
minMax[i][0] = std::make_pair(
|
|
*(minmaxTranslation.first),
|
|
*(minmaxTranslation.second)
|
|
);
|
|
|
|
auto minmaxRotation = std::minmax_element(
|
|
std::begin(entry.updateRotation),
|
|
std::end(entry.updateRotation)
|
|
);
|
|
minMax[i][1] = std::make_pair(
|
|
*(minmaxRotation.first),
|
|
*(minmaxRotation.second)
|
|
);
|
|
|
|
auto minmaxScaling = std::minmax_element(
|
|
std::begin(entry.updateScaling),
|
|
std::end(entry.updateScaling)
|
|
);
|
|
minMax[i][2] = std::make_pair(
|
|
*(minmaxScaling.first),
|
|
*(minmaxScaling.second)
|
|
);
|
|
|
|
auto minmaxUpdateRenderable = std::minmax_element(
|
|
std::begin(entry.updateRenderable),
|
|
std::end(entry.updateRenderable)
|
|
);
|
|
minMax[i][3] = std::make_pair(
|
|
*(minmaxUpdateRenderable.first),
|
|
*(minmaxUpdateRenderable.second)
|
|
);
|
|
|
|
auto minmaxRendering = std::minmax_element(
|
|
std::begin(entry.renderTime),
|
|
std::end(entry.renderTime)
|
|
);
|
|
minMax[i][4] = std::make_pair(
|
|
*(minmaxRendering.first),
|
|
*(minmaxRendering.second)
|
|
);
|
|
|
|
|
|
}
|
|
|
|
// If we don't want to sort, we will leave the indices list alone, thus
|
|
// leaving them in the regular ordering
|
|
Sorting selection = Sorting(sorting);
|
|
if (selection != Sorting::NoSorting) {
|
|
std::function<bool(size_t, size_t)> sortFunc;
|
|
|
|
if (selection == Sorting::Total) {
|
|
// If we do want to sort totally, we need to sum all the averages and
|
|
// use that as the criterion
|
|
sortFunc = [&averages](size_t a, size_t b) {
|
|
float sumA = std::accumulate(
|
|
std::begin(averages[a]),
|
|
std::end(averages[a]),
|
|
0.f
|
|
);
|
|
|
|
float sumB = std::accumulate(
|
|
std::begin(averages[b]),
|
|
std::end(averages[b]),
|
|
0.f
|
|
);
|
|
|
|
return sumA > sumB;
|
|
};
|
|
}
|
|
else {
|
|
// otherwise we use the sorting index
|
|
int sel = _sortingSelection;
|
|
sortFunc = [sel, &averages](size_t a, size_t b) {
|
|
return averages[a][sel] > averages[b][sel];
|
|
};
|
|
}
|
|
|
|
std::sort(
|
|
indices.begin(),
|
|
indices.end(),
|
|
sortFunc
|
|
);
|
|
}
|
|
else {
|
|
// NoSorting -> So we sort by names instead
|
|
std::sort(
|
|
indices.begin(),
|
|
indices.end(),
|
|
[layout](size_t a, size_t b) {
|
|
return std::string(layout->sceneGraphEntries[a].name) < std::string(layout->sceneGraphEntries[b].name);
|
|
}
|
|
);
|
|
}
|
|
|
|
for (int i = 0; i < layout->nScaleGraphEntries; ++i) {
|
|
// We are using the indices list as an additional level of indirection
|
|
// into the respective values so that the list will be sorted by whatever
|
|
// criterion we selected previously
|
|
|
|
const PerformanceLayout::SceneGraphPerformanceLayout& entry =
|
|
layout->sceneGraphEntries[indices[i]];
|
|
|
|
if (ImGui::CollapsingHeader(entry.name)) {
|
|
std::string updateTranslationTime = std::to_string(
|
|
entry.updateTranslation[PerformanceLayout::NumberValues - 1]
|
|
) + "us";
|
|
|
|
ImGui::PlotLines(
|
|
fmt::format(
|
|
"UpdateTranslation\nAverage: {}us",
|
|
averages[indices[i]][0]
|
|
).c_str(),
|
|
&entry.updateTranslation[0],
|
|
PerformanceLayout::NumberValues,
|
|
0,
|
|
updateTranslationTime.c_str(),
|
|
minMax[indices[i]][0].first,
|
|
minMax[indices[i]][0].second,
|
|
ImVec2(0, 40)
|
|
);
|
|
|
|
std::string updateRotationTime = std::to_string(
|
|
entry.updateRotation[PerformanceLayout::NumberValues - 1]
|
|
) + "us";
|
|
|
|
ImGui::PlotLines(
|
|
fmt::format(
|
|
"UpdateRotation\nAverage: {}us",
|
|
averages[indices[i]][1]
|
|
).c_str(),
|
|
&entry.updateRotation[0],
|
|
PerformanceLayout::NumberValues,
|
|
0,
|
|
updateRotationTime.c_str(),
|
|
minMax[indices[i]][1].first,
|
|
minMax[indices[i]][1].second,
|
|
ImVec2(0, 40)
|
|
);
|
|
|
|
std::string updateScalingTime = std::to_string(
|
|
entry.updateScaling[PerformanceLayout::NumberValues - 1]
|
|
) + "us";
|
|
|
|
ImGui::PlotLines(
|
|
fmt::format(
|
|
"UpdateScaling\nAverage: {}us",
|
|
averages[indices[i]][2]
|
|
).c_str(),
|
|
&entry.updateScaling[0],
|
|
PerformanceLayout::NumberValues,
|
|
0,
|
|
updateScalingTime.c_str(),
|
|
minMax[indices[i]][2].first,
|
|
minMax[indices[i]][2].second,
|
|
ImVec2(0, 40)
|
|
);
|
|
|
|
std::string updateRenderableTime = std::to_string(
|
|
entry.updateRenderable[PerformanceLayout::NumberValues - 1]
|
|
) + "us";
|
|
|
|
ImGui::PlotLines(
|
|
fmt::format(
|
|
"UpdateRender\nAverage: {}us",
|
|
averages[indices[i]][3]
|
|
).c_str(),
|
|
&entry.updateRenderable[0],
|
|
PerformanceLayout::NumberValues,
|
|
0,
|
|
updateRenderableTime.c_str(),
|
|
minMax[indices[i]][3].first,
|
|
minMax[indices[i]][3].second,
|
|
ImVec2(0, 40)
|
|
);
|
|
|
|
std::string renderTime = std::to_string(
|
|
entry.renderTime[PerformanceLayout::NumberValues - 1]
|
|
) + "us";
|
|
|
|
ImGui::PlotLines(
|
|
fmt::format(
|
|
"RenderTime\nAverage: {}us",
|
|
averages[indices[i]][4]
|
|
).c_str(),
|
|
&entry.renderTime[0],
|
|
PerformanceLayout::NumberValues,
|
|
0,
|
|
renderTime.c_str(),
|
|
minMax[indices[i]][4].first,
|
|
minMax[indices[i]][4].second,
|
|
ImVec2(0, 40)
|
|
);
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
if (_functionsIsEnabled) {
|
|
bool v = _functionsIsEnabled;
|
|
ImGui::Begin("Functions", &v);
|
|
_functionsIsEnabled = v;
|
|
using namespace performance;
|
|
|
|
for (int i = 0; i < layout->nFunctionEntries; ++i) {
|
|
const PerformanceLayout::FunctionPerformanceLayout& entry =
|
|
layout->functionEntries[i];
|
|
|
|
float avg = 0.f;
|
|
int count = 0;
|
|
for (int j = 0; j < PerformanceLayout::NumberValues; ++j) {
|
|
avg += layout->functionEntries[i].time[j];
|
|
if (layout->functionEntries[i].time[j] != 0.f)
|
|
++count;
|
|
}
|
|
avg /= count;
|
|
|
|
auto minmax = std::minmax_element(
|
|
std::begin(layout->functionEntries[i].time),
|
|
std::end(layout->functionEntries[i].time)
|
|
);
|
|
|
|
const PerformanceLayout::FunctionPerformanceLayout& f =
|
|
layout->functionEntries[i];
|
|
|
|
std::string renderTime = std::to_string(
|
|
entry.time[PerformanceLayout::NumberValues - 1]
|
|
) + "us";
|
|
ImGui::PlotLines(
|
|
fmt::format("{}\nAverage: {}us", entry.name, avg).c_str(),
|
|
&entry.time[0],
|
|
PerformanceLayout::NumberValues,
|
|
0,
|
|
renderTime.c_str(),
|
|
*(minmax.first),
|
|
*(minmax.second),
|
|
ImVec2(0, 40)
|
|
);
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
} // namespace gui
|
|
} // namespace openspace
|