Move Caffe simulation to a separate thread.
Teapot now shows pretty much instantly after starting the program, and then only reappears when switching inputs.
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include "visualisations.hpp"
|
#include "visualisations.hpp"
|
||||||
#include "Range.hpp"
|
#include "Range.hpp"
|
||||||
#include "glutils.hpp"
|
#include "glutils.hpp"
|
||||||
|
#include "Simulator.hpp"
|
||||||
|
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
|
|
||||||
@@ -228,25 +229,17 @@ void RenderingState::handleMouseAt(int x, int y)
|
|||||||
glutPostRedisplay();
|
glutPostRedisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingState::loadSimulationData(const std::map<string, LayerInfo> &info, vector<vector<LayerData>> &&data)
|
|
||||||
{
|
|
||||||
layerInfo = std::move(info);
|
|
||||||
layerData = std::move(data);
|
|
||||||
currentData = layerData.begin();
|
|
||||||
|
|
||||||
queueUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingState::queueUpdate()
|
void RenderingState::queueUpdate()
|
||||||
{
|
{
|
||||||
// Make sure that visualisations are cleared in the current thread
|
// Make sure that visualisations are cleared in the current thread
|
||||||
layerVisualisations.clear();
|
layerVisualisations.clear();
|
||||||
interactionAnimations.clear();
|
interactionAnimations.clear();
|
||||||
|
|
||||||
loadingFuture = std::async(std::launch::async, []() {
|
visualisationFuture = std::async(std::launch::async, []() {
|
||||||
RenderingState::instance().updateVisualisers();
|
RenderingState::instance().updateVisualisers();
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
isLoading = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingState::updateVisualisers()
|
void RenderingState::updateVisualisers()
|
||||||
@@ -278,7 +271,7 @@ void RenderingState::render(float time) const
|
|||||||
// Clear Color and Depth Buffers
|
// Clear Color and Depth Buffers
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
if (!isLoading) {
|
if (!isLoading()) {
|
||||||
renderVisualisation(time);
|
renderVisualisation(time);
|
||||||
} else {
|
} else {
|
||||||
renderLoadingScreen();
|
renderLoadingScreen();
|
||||||
@@ -418,6 +411,8 @@ void RenderingState::loadOptions(const Options &programOptions)
|
|||||||
options.pathColor = programOptions.pathColor();
|
options.pathColor = programOptions.pathColor();
|
||||||
options.layerAlpha = programOptions.layerTransparancy();
|
options.layerAlpha = programOptions.layerTransparancy();
|
||||||
options.interactionAlpha = programOptions.interactionTransparancy();
|
options.interactionAlpha = programOptions.interactionTransparancy();
|
||||||
|
|
||||||
|
simulationFuture = std::async(std::launch::async, Simulator::loadSimulationData, programOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Color &RenderingState::pathColor() const
|
const Color &RenderingState::pathColor() const
|
||||||
@@ -454,24 +449,47 @@ float RenderingState::layerAlpha() const
|
|||||||
return options.layerAlpha;
|
return options.layerAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingState::idleFunc()
|
/**
|
||||||
|
* Attempt to wait for completion of a future, for less than a frame.
|
||||||
|
*
|
||||||
|
* @tparam T
|
||||||
|
* @param f The future to wait for
|
||||||
|
* @return The result of the computation, or an empty optional if it hasn't finished.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
static std::optional<T> awaitCompletion(std::future<T>& f)
|
||||||
{
|
{
|
||||||
if (isLoading && loadingFuture.valid()) {
|
constexpr auto waitTime = std::chrono::milliseconds(10);
|
||||||
auto result = loadingFuture.wait_for(std::chrono::milliseconds(16));
|
switch (f.wait_for(waitTime)) {
|
||||||
switch (result) {
|
|
||||||
case std::future_status::deferred:
|
|
||||||
LOG(ERROR) << "loading status was deferred, invalid state!";
|
|
||||||
abort();
|
|
||||||
|
|
||||||
case std::future_status::timeout:
|
case std::future_status::timeout:
|
||||||
// Still loading
|
return std::nullopt;
|
||||||
break;
|
|
||||||
|
|
||||||
case std::future_status::ready:
|
case std::future_status::ready:
|
||||||
loadingFuture.get();
|
return f.get();
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "loading status was deferred, invalid state!";
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RenderingState::idleFunc()
|
||||||
|
{
|
||||||
|
if (isLoading()) {
|
||||||
|
if (visualisationFuture.valid()) {
|
||||||
|
auto result = awaitCompletion(visualisationFuture);
|
||||||
|
if (result) {
|
||||||
loadGLItems();
|
loadGLItems();
|
||||||
isLoading = false;
|
return;
|
||||||
break;
|
}
|
||||||
|
} else if (simulationFuture.valid()) {
|
||||||
|
auto result = awaitCompletion(simulationFuture);
|
||||||
|
if (result) {
|
||||||
|
tie(layerInfo, layerData) = std::move(*result);
|
||||||
|
currentData = layerData.begin();
|
||||||
|
queueUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (options.mouse_1_pressed) {
|
if (options.mouse_1_pressed) {
|
||||||
@@ -490,3 +508,8 @@ void RenderingState::loadGLItems()
|
|||||||
std::for_each(layerVisualisations.begin(), layerVisualisations.end(), [](auto& x) { x->glLoad(); });
|
std::for_each(layerVisualisations.begin(), layerVisualisations.end(), [](auto& x) { x->glLoad(); });
|
||||||
std::for_each(interactionAnimations.begin(), interactionAnimations.end(), [](auto& x) { if (x) x->glLoad(); });
|
std::for_each(interactionAnimations.begin(), interactionAnimations.end(), [](auto& x) { if (x) x->glLoad(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RenderingState::isLoading() const
|
||||||
|
{
|
||||||
|
return visualisationFuture.valid() || simulationFuture.valid();
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace fmri
|
|||||||
void handleSpecialKey(int key);
|
void handleSpecialKey(int key);
|
||||||
void render(float time) const;
|
void render(float time) const;
|
||||||
|
|
||||||
void loadSimulationData(const std::map<string, LayerInfo> &info, std::vector<std::vector<LayerData>> &&data);
|
|
||||||
/**
|
/**
|
||||||
* Load rendering-specific options from the given options object.
|
* Load rendering-specific options from the given options object.
|
||||||
*
|
*
|
||||||
@@ -79,8 +78,8 @@ namespace fmri
|
|||||||
std::vector<std::vector<LayerData>>::iterator currentData;
|
std::vector<std::vector<LayerData>>::iterator currentData;
|
||||||
std::vector<std::unique_ptr<LayerVisualisation>> layerVisualisations;
|
std::vector<std::unique_ptr<LayerVisualisation>> layerVisualisations;
|
||||||
std::vector<std::unique_ptr<Animation>> interactionAnimations;
|
std::vector<std::unique_ptr<Animation>> interactionAnimations;
|
||||||
bool isLoading = false;
|
std::future<bool> visualisationFuture;
|
||||||
std::future<void> loadingFuture;
|
std::future<std::pair<decltype(layerInfo), decltype(layerData)>> simulationFuture;
|
||||||
|
|
||||||
|
|
||||||
RenderingState() noexcept;
|
RenderingState() noexcept;
|
||||||
@@ -102,5 +101,7 @@ namespace fmri
|
|||||||
void renderVisualisation(float time) const;
|
void renderVisualisation(float time) const;
|
||||||
|
|
||||||
void loadGLItems();
|
void loadGLItems();
|
||||||
|
|
||||||
|
bool isLoading() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,3 +231,34 @@ const map<string, LayerInfo> & Simulator::layerInfo() const
|
|||||||
{
|
{
|
||||||
return pImpl->layerInfo();
|
return pImpl->layerInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<std::map<std::string, LayerInfo>, std::vector<std::vector<LayerData>>>
|
||||||
|
fmri::Simulator::loadSimulationData(const Options &options)
|
||||||
|
{
|
||||||
|
Simulator simulator(options.model(), options.weights(), options.means());
|
||||||
|
|
||||||
|
std::vector<std::vector<LayerData>> results;
|
||||||
|
transform(options.inputs().begin(), options.inputs().end(), back_inserter(results), [&simulator] (auto& x) {
|
||||||
|
return simulator.simulate(x);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto dumper = options.imageDumper();
|
||||||
|
if (dumper) {
|
||||||
|
for (auto &layer : *results.begin()) {
|
||||||
|
dumper->dump(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto optLabels = options.labels();
|
||||||
|
|
||||||
|
if (optLabels) {
|
||||||
|
auto& labels = *optLabels;
|
||||||
|
for (const auto& result : results) {
|
||||||
|
auto &last = *result.rbegin();
|
||||||
|
auto bestIndex = std::distance(last.data(), max_element(last.data(), last.data() + last.numEntries()));
|
||||||
|
LOG(INFO) << "Got answer: " << labels[bestIndex] << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_pair(simulator.layerInfo(), std::move(results));
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "LayerData.hpp"
|
#include "LayerData.hpp"
|
||||||
#include "LayerInfo.hpp"
|
#include "LayerInfo.hpp"
|
||||||
|
#include "Options.hpp"
|
||||||
|
|
||||||
namespace fmri {
|
namespace fmri {
|
||||||
using std::string;
|
using std::string;
|
||||||
@@ -18,6 +19,8 @@ namespace fmri {
|
|||||||
vector<LayerData> simulate(const string &input_file);
|
vector<LayerData> simulate(const string &input_file);
|
||||||
const std::map<std::string, LayerInfo>& layerInfo() const;
|
const std::map<std::string, LayerInfo>& layerInfo() const;
|
||||||
|
|
||||||
|
static std::pair<std::map<std::string, LayerInfo>, std::vector<std::vector<LayerData>>> loadSimulationData(const Options &options);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> pImpl;
|
std::unique_ptr<Impl> pImpl;
|
||||||
|
|||||||
@@ -17,39 +17,6 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
|
|
||||||
static void loadSimulationData(const Options &options)
|
|
||||||
{
|
|
||||||
vector<vector<LayerData>> results;
|
|
||||||
|
|
||||||
auto dumper = options.imageDumper();
|
|
||||||
Simulator simulator(options.model(), options.weights(), options.means());
|
|
||||||
|
|
||||||
for (const auto &image : options.inputs()) {
|
|
||||||
results.emplace_back(simulator.simulate(image));
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK_GT(results.size(), 0) << "Should have some results" << endl;
|
|
||||||
|
|
||||||
if (dumper) {
|
|
||||||
for (auto &layer : *results.begin()) {
|
|
||||||
dumper->dump(layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto optLabels = options.labels();
|
|
||||||
|
|
||||||
if (optLabels) {
|
|
||||||
auto& labels = *optLabels;
|
|
||||||
for (const auto& result : results) {
|
|
||||||
auto &last = *result.rbegin();
|
|
||||||
auto bestIndex = std::distance(last.data(), max_element(last.data(), last.data() + last.numEntries()));
|
|
||||||
LOG(INFO) << "Got answer: " << labels[bestIndex] << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderingState::instance().loadSimulationData(simulator.layerInfo(), std::move(results));
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
google::InitGoogleLogging(argv[0]);
|
google::InitGoogleLogging(argv[0]);
|
||||||
@@ -64,7 +31,6 @@ int main(int argc, char *argv[])
|
|||||||
// Prepare data for simulations
|
// Prepare data for simulations
|
||||||
Options options(argc, argv);
|
Options options(argc, argv);
|
||||||
RenderingState::instance().loadOptions(options);
|
RenderingState::instance().loadOptions(options);
|
||||||
loadSimulationData(options);
|
|
||||||
|
|
||||||
// Register callbacks
|
// Register callbacks
|
||||||
glutReshapeFunc(changeWindowSize);
|
glutReshapeFunc(changeWindowSize);
|
||||||
|
|||||||
Reference in New Issue
Block a user