Large refactor of texture loading.
In this new implementation, everything is loaded on a separate thread, but the textures are initialized on the main OpenGL thread. This is all to work around the fact that you cannot create a separate GL context in GLUT.
This commit is contained in:
@@ -14,3 +14,8 @@ void fmri::Drawable::patchTransparancy()
|
|||||||
(*it)[3] = alpha;
|
(*it)[3] = alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fmri::Drawable::glLoad()
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace fmri
|
|||||||
|
|
||||||
virtual void draw(float time) = 0;
|
virtual void draw(float time) = 0;
|
||||||
virtual void patchTransparancy();
|
virtual void patchTransparancy();
|
||||||
|
virtual void glLoad();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<Color> colorBuffer;
|
std::vector<Color> colorBuffer;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ void ImageInteractionAnimation::draw(float step)
|
|||||||
|
|
||||||
ImageInteractionAnimation::ImageInteractionAnimation(const DType *data, const std::vector<int> &shape, const std::vector<float> &prevPositions,
|
ImageInteractionAnimation::ImageInteractionAnimation(const DType *data, const std::vector<int> &shape, const std::vector<float> &prevPositions,
|
||||||
const std::vector<float> &curPositions) :
|
const std::vector<float> &curPositions) :
|
||||||
texture(loadTexture(data, shape[2], shape[1] * shape[3], shape[1])),
|
texture(data, shape[2], shape[1] * shape[3], GL_LUMINANCE, shape[1]),
|
||||||
startingPositions(MultiImageVisualisation::getVertices(prevPositions)),
|
startingPositions(MultiImageVisualisation::getVertices(prevPositions)),
|
||||||
deltas(MultiImageVisualisation::getVertices(curPositions)),
|
deltas(MultiImageVisualisation::getVertices(curPositions)),
|
||||||
textureCoordinates(MultiImageVisualisation::getTexCoords(shape[1]))
|
textureCoordinates(MultiImageVisualisation::getTexCoords(shape[1]))
|
||||||
@@ -26,3 +26,10 @@ ImageInteractionAnimation::ImageInteractionAnimation(const DType *data, const st
|
|||||||
deltas[i] = LAYER_X_OFFSET;
|
deltas[i] = LAYER_X_OFFSET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageInteractionAnimation::glLoad()
|
||||||
|
{
|
||||||
|
Drawable::glLoad();
|
||||||
|
|
||||||
|
texture.configure(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ namespace fmri
|
|||||||
public:
|
public:
|
||||||
ImageInteractionAnimation(const DType *data, const std::vector<int> &shape, const std::vector<float> &prevPositions,
|
ImageInteractionAnimation(const DType *data, const std::vector<int> &shape, const std::vector<float> &prevPositions,
|
||||||
const std::vector<float> &curPositions);
|
const std::vector<float> &curPositions);
|
||||||
virtual void draw(float step);
|
void draw(float step) override;
|
||||||
|
void glLoad() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Texture texture;
|
Texture texture;
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ using namespace std;
|
|||||||
* @param data Layer data to generate an image for.
|
* @param data Layer data to generate an image for.
|
||||||
* @return A normalized RGB image, stored in a vector.
|
* @return A normalized RGB image, stored in a vector.
|
||||||
*/
|
*/
|
||||||
static vector<float> getRGBImage(const LayerData &data)
|
static unique_ptr<float[]> getRGBImage(const LayerData &data)
|
||||||
{
|
{
|
||||||
|
CHECK_EQ(data.shape().size(), 4) << "Should be image-like-layer.";
|
||||||
|
CHECK_EQ(data.shape()[0], 1) << "Should be a single image";
|
||||||
vector<cv::Mat> channels;
|
vector<cv::Mat> channels;
|
||||||
const int numPixels = data.shape()[2] * data.shape()[3];
|
const int numPixels = data.shape()[2] * data.shape()[3];
|
||||||
for (auto i : Range(3)) {
|
for (auto i : Range(3)) {
|
||||||
@@ -29,7 +31,7 @@ static vector<float> getRGBImage(const LayerData &data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat channel(data.shape()[3], data.shape()[2], CV_32FC1);
|
cv::Mat channel(data.shape()[3], data.shape()[2], CV_32FC1);
|
||||||
copy(data.data() + i * numPixels, data.data() + (i + 1) * numPixels, channel.begin<float>());
|
copy_n(data.data() + i * numPixels, numPixels, channel.begin<float>());
|
||||||
channels.push_back(channel);
|
channels.push_back(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,19 +42,18 @@ static vector<float> getRGBImage(const LayerData &data)
|
|||||||
|
|
||||||
outImage = outImage.reshape(1);
|
outImage = outImage.reshape(1);
|
||||||
|
|
||||||
vector<float> final(outImage.begin<float>(), outImage.end<float>());
|
auto final = make_unique<float[]>(data.numEntries());
|
||||||
rescale(final.begin(), final.end(), 0.f, 1.f);
|
std::copy_n(outImage.begin<float>(), data.numEntries(), final.get());
|
||||||
|
|
||||||
return final;
|
return final;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputLayerVisualisation::InputLayerVisualisation(const LayerData &data)
|
InputLayerVisualisation::InputLayerVisualisation(const LayerData &data) :
|
||||||
|
width(data.shape().at(2)),
|
||||||
|
height(data.shape().at(3)),
|
||||||
|
texture(getRGBImage(data), width, height, GL_RGB)
|
||||||
{
|
{
|
||||||
CHECK_EQ(data.shape().size(), 4) << "Should be image-like-layer." << endl;
|
const auto channels = data.shape()[1];
|
||||||
auto imageData = getRGBImage(data);
|
|
||||||
|
|
||||||
const auto images = data.shape()[0], channels = data.shape()[1], width = data.shape()[2], height = data.shape()[3];
|
|
||||||
CHECK_EQ(images, 1) << "Should be single image" << endl;
|
|
||||||
|
|
||||||
targetWidth = width / 5.f;
|
targetWidth = width / 5.f;
|
||||||
targetHeight = width / 5.f;
|
targetHeight = width / 5.f;
|
||||||
@@ -61,9 +62,6 @@ InputLayerVisualisation::InputLayerVisualisation(const LayerData &data)
|
|||||||
for (auto i : Range(3, 3 * channels)) {
|
for (auto i : Range(3, 3 * channels)) {
|
||||||
nodePositions_.push_back(nodePositions_[i % 3]);
|
nodePositions_.push_back(nodePositions_[i % 3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
texture.configure(GL_TEXTURE_2D);
|
|
||||||
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, GL_RGB, GL_FLOAT, imageData.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputLayerVisualisation::draw(float)
|
void InputLayerVisualisation::draw(float)
|
||||||
@@ -86,3 +84,10 @@ void InputLayerVisualisation::draw(float)
|
|||||||
|
|
||||||
drawImageTiles(4, vertices, texCoords, texture, alpha);
|
drawImageTiles(4, vertices, texCoords, texture, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputLayerVisualisation::glLoad()
|
||||||
|
{
|
||||||
|
Drawable::glLoad();
|
||||||
|
|
||||||
|
texture.configure(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,10 +13,15 @@ namespace fmri
|
|||||||
|
|
||||||
void draw(float time) override;
|
void draw(float time) override;
|
||||||
|
|
||||||
|
void glLoad() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Texture texture;
|
|
||||||
float targetWidth;
|
float targetWidth;
|
||||||
float targetHeight;
|
float targetHeight;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
Texture texture;
|
||||||
|
std::vector<float> textureBuffer;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,16 @@
|
|||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer)
|
MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer) :
|
||||||
|
texture(layer.data(), layer.shape().at(2), layer.shape().at(3) * layer.shape().at(1), GL_LUMINANCE, layer.shape().at(1))
|
||||||
{
|
{
|
||||||
auto dimensions = layer.shape();
|
auto dimensions = layer.shape();
|
||||||
CHECK_EQ(4, dimensions.size()) << "Should be image-like layer";
|
|
||||||
|
|
||||||
const auto images = dimensions[0],
|
const auto images = dimensions[0],
|
||||||
channels = dimensions[1],
|
channels = dimensions[1];
|
||||||
width = dimensions[2],
|
|
||||||
height = dimensions[3];
|
|
||||||
|
|
||||||
CHECK_EQ(1, images) << "Only single input image is supported" << endl;
|
CHECK_EQ(1, images) << "Only single input image is supported" << endl;
|
||||||
|
|
||||||
texture = loadTexture(layer.data(), width, channels * height, channels);
|
|
||||||
initNodePositions<Ordering::SQUARE>(channels, 3);
|
initNodePositions<Ordering::SQUARE>(channels, 3);
|
||||||
vertexBuffer = getVertices(nodePositions_);
|
vertexBuffer = getVertices(nodePositions_);
|
||||||
texCoordBuffer = getTexCoords(channels);
|
texCoordBuffer = getTexCoords(channels);
|
||||||
@@ -65,3 +62,10 @@ std::vector<float> MultiImageVisualisation::getTexCoords(int n)
|
|||||||
|
|
||||||
return coords;
|
return coords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiImageVisualisation::glLoad()
|
||||||
|
{
|
||||||
|
Drawable::glLoad();
|
||||||
|
|
||||||
|
texture.configure(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace fmri
|
|||||||
|
|
||||||
void draw(float time) override;
|
void draw(float time) override;
|
||||||
|
|
||||||
|
void glLoad() override;
|
||||||
|
|
||||||
static vector<float> getVertices(const std::vector<float> &nodePositions, float scaling = 1);
|
static vector<float> getVertices(const std::vector<float> &nodePositions, float scaling = 1);
|
||||||
static std::vector<float> getTexCoords(int n);
|
static std::vector<float> getTexCoords(int n);
|
||||||
|
|
||||||
|
|||||||
@@ -41,5 +41,13 @@ Texture PoolingLayerAnimation::loadTextureForData(const LayerData &data)
|
|||||||
CHECK_EQ(data.shape()[0], 1) << "Only single images supported";
|
CHECK_EQ(data.shape()[0], 1) << "Only single images supported";
|
||||||
|
|
||||||
auto channels = data.shape()[1], width = data.shape()[2], height = data.shape()[3];
|
auto channels = data.shape()[1], width = data.shape()[2], height = data.shape()[3];
|
||||||
return loadTexture(data.data(), width, height * channels, channels);
|
return Texture(data.data(), width, height * channels, GL_LUMINANCE, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoolingLayerAnimation::glLoad()
|
||||||
|
{
|
||||||
|
Drawable::glLoad();
|
||||||
|
|
||||||
|
original.configure(GL_TEXTURE_2D);
|
||||||
|
downSampled.configure(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace fmri
|
|||||||
const std::vector<float> &curPositions);
|
const std::vector<float> &curPositions);
|
||||||
|
|
||||||
void draw(float timeStep) override;
|
void draw(float timeStep) override;
|
||||||
|
void glLoad() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Texture original;
|
Texture original;
|
||||||
|
|||||||
@@ -223,9 +223,7 @@ void RenderingState::loadSimulationData(const std::map<string, LayerInfo> &info,
|
|||||||
void RenderingState::queueUpdate()
|
void RenderingState::queueUpdate()
|
||||||
{
|
{
|
||||||
loadingFuture = std::async(std::launch::async, []() {
|
loadingFuture = std::async(std::launch::async, []() {
|
||||||
// Currently causes a segfault, due to threaded OpenGL.
|
RenderingState::instance().updateVisualisers();
|
||||||
// Possible solution: don't do GL things while loading.
|
|
||||||
//RenderingState::instance().updateVisualisers();
|
|
||||||
});
|
});
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
}
|
}
|
||||||
@@ -456,7 +454,7 @@ float RenderingState::layerAlpha() const
|
|||||||
void RenderingState::idleFunc()
|
void RenderingState::idleFunc()
|
||||||
{
|
{
|
||||||
if (isLoading && loadingFuture.valid()) {
|
if (isLoading && loadingFuture.valid()) {
|
||||||
auto result = loadingFuture.wait_for(std::chrono::milliseconds(40));
|
auto result = loadingFuture.wait_for(std::chrono::milliseconds(16));
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case std::future_status::deferred:
|
case std::future_status::deferred:
|
||||||
LOG(ERROR) << "loading status was deferred, invalid state!";
|
LOG(ERROR) << "loading status was deferred, invalid state!";
|
||||||
@@ -468,7 +466,8 @@ void RenderingState::idleFunc()
|
|||||||
|
|
||||||
case std::future_status::ready:
|
case std::future_status::ready:
|
||||||
loadingFuture.get();
|
loadingFuture.get();
|
||||||
//isLoading = false;
|
loadGLItems();
|
||||||
|
isLoading = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -484,3 +483,9 @@ void RenderingState::idleFunc()
|
|||||||
}
|
}
|
||||||
glutPostRedisplay();
|
glutPostRedisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderingState::loadGLItems()
|
||||||
|
{
|
||||||
|
std::for_each(layerVisualisations.begin(), layerVisualisations.end(), [](auto& x) { x->glLoad(); });
|
||||||
|
std::for_each(interactionAnimations.begin(), interactionAnimations.end(), [](auto& x) { if (x) x->glLoad(); });
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,5 +102,7 @@ namespace fmri
|
|||||||
void renderLoadingScreen() const;
|
void renderLoadingScreen() const;
|
||||||
|
|
||||||
void renderVisualisation(float time) const;
|
void renderVisualisation(float time) const;
|
||||||
|
|
||||||
|
void loadGLItems();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <glog/logging.h>
|
||||||
|
#include <GL/glu.h>
|
||||||
#include "Texture.hpp"
|
#include "Texture.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
|
|
||||||
Texture::Texture() noexcept
|
|
||||||
{
|
|
||||||
glGenTextures(1, &id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture::Texture(GLuint id) noexcept : id(id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture::~Texture()
|
Texture::~Texture()
|
||||||
{
|
{
|
||||||
if (id != 0) {
|
if (id != 0) {
|
||||||
@@ -22,21 +16,23 @@ Texture::~Texture()
|
|||||||
Texture &Texture::operator=(Texture && other) noexcept
|
Texture &Texture::operator=(Texture && other) noexcept
|
||||||
{
|
{
|
||||||
std::swap(id, other.id);
|
std::swap(id, other.id);
|
||||||
|
std::swap(width, other.width);
|
||||||
|
std::swap(height, other.height);
|
||||||
|
std::swap(data, other.data);
|
||||||
|
std::swap(format, other.format);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Texture(Texture && other) noexcept
|
|
||||||
{
|
|
||||||
std::swap(id, other.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::bind(GLenum target) const
|
void Texture::bind(GLenum target) const
|
||||||
{
|
{
|
||||||
|
CHECK_NE(id, 0) << "Texture doesn't hold a reference!";
|
||||||
glBindTexture(target, id);
|
glBindTexture(target, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::configure(GLenum target) const
|
void Texture::configure(GLenum target)
|
||||||
{
|
{
|
||||||
|
ensureReference();
|
||||||
|
CHECK(data) << "No valid data to configure with";
|
||||||
bind(target);
|
bind(target);
|
||||||
const float color[] = {1, 0, 1}; // Background color for textures.
|
const float color[] = {1, 0, 1}; // Background color for textures.
|
||||||
|
|
||||||
@@ -48,4 +44,64 @@ void Texture::configure(GLenum target) const
|
|||||||
// Set up texture scaling
|
// Set up texture scaling
|
||||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); // Use mipmapping for scaling down
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); // Use mipmapping for scaling down
|
||||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Use nearest pixel when scaling up.
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Use nearest pixel when scaling up.
|
||||||
|
gluBuild2DMipmaps(target, format, width, height, format, GL_FLOAT, data.get());
|
||||||
|
data.reset(); // Release data buffer.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::ensureReference()
|
||||||
|
{
|
||||||
|
if (id == 0) {
|
||||||
|
glGenTextures(1, &id);
|
||||||
|
CHECK_NE(id, 0) << "Failed to allocate a texture.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::Texture(const float *data, int width, int height, GLuint format, int subImages) :
|
||||||
|
Texture(std::make_unique<float[]>(width * height), width, height, format, subImages)
|
||||||
|
{
|
||||||
|
std::copy_n(data, width * height, this->data.get());
|
||||||
|
preCalc(subImages);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::Texture(std::unique_ptr<float[]> &&data, int width, int height, GLuint format, int subImages) :
|
||||||
|
id(0),
|
||||||
|
width(width),
|
||||||
|
height(height),
|
||||||
|
format(format),
|
||||||
|
data(std::move(data))
|
||||||
|
{
|
||||||
|
preCalc(subImages);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::preCalc(int subImages)
|
||||||
|
{
|
||||||
|
CHECK_EQ(height % subImages, 0) << "Image should be properly divisible!";
|
||||||
|
|
||||||
|
// Rescale images
|
||||||
|
const auto step = width * height * getStride() / subImages;
|
||||||
|
auto cur = data.get();
|
||||||
|
for (auto i = 0; i < subImages; ++i) {
|
||||||
|
rescale(cur, cur + step, 0, 1);
|
||||||
|
std::advance(cur, step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::getStride()
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case GL_RGB:
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case GL_LUMINANCE:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Stride unknown for format: " << format;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::Texture() noexcept :
|
||||||
|
id(0)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
|
|
||||||
namespace fmri
|
namespace fmri
|
||||||
@@ -13,19 +14,12 @@ namespace fmri
|
|||||||
class Texture
|
class Texture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Allocate a new texture
|
|
||||||
*/
|
|
||||||
Texture() noexcept;
|
Texture() noexcept;
|
||||||
|
Texture(const float* data, int width, int height, GLuint format, int subImages = 1);
|
||||||
|
Texture(std::unique_ptr<float[]> &&data, int width, int height, GLuint format, int subImages = 1);
|
||||||
Texture(Texture &&) noexcept;
|
Texture(Texture &&) noexcept;
|
||||||
Texture(const Texture &) = delete;
|
Texture(const Texture &) = delete;
|
||||||
|
|
||||||
/**
|
|
||||||
* Own an existing texture
|
|
||||||
* @param id original texture ID.
|
|
||||||
*/
|
|
||||||
explicit Texture(GLuint id) noexcept;
|
|
||||||
|
|
||||||
~Texture();
|
~Texture();
|
||||||
|
|
||||||
Texture &operator=(Texture &&) noexcept;
|
Texture &operator=(Texture &&) noexcept;
|
||||||
@@ -36,10 +30,19 @@ namespace fmri
|
|||||||
* @param target valid target for glBindTexture.
|
* @param target valid target for glBindTexture.
|
||||||
*/
|
*/
|
||||||
void bind(GLenum target) const;
|
void bind(GLenum target) const;
|
||||||
void configure(GLenum target) const;
|
void configure(GLenum target);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint id;
|
GLuint id;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
GLuint format;
|
||||||
|
std::unique_ptr<float[]> data;
|
||||||
|
|
||||||
|
void ensureReference();
|
||||||
|
|
||||||
|
void preCalc(int subImages);
|
||||||
|
|
||||||
|
int getStride();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user