summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2017-03-29 19:28:20 +0200
committerStanislaw Halik <sthalik@misaki.pl>2017-03-29 19:28:27 +0200
commit87c02a5399abb0dfe020f3cd1c5e2337c0d8cb8f (patch)
treeed82c1509ca562b49aee7df440ce575ceb3beb88
parent94f510f31c3f5ca79f24f2daa085cb34d5650565 (diff)
pose-widget: reduce latency
- project on a separate thread - use Qt's native ARGB32 - use double buffering - more fine-grained locks
-rw-r--r--gui/main-window.ui4
-rw-r--r--pose-widget/glwidget.cpp113
-rw-r--r--pose-widget/glwidget.h66
3 files changed, 143 insertions, 40 deletions
diff --git a/gui/main-window.ui b/gui/main-window.ui
index db75a20d..06375f76 100644
--- a/gui/main-window.ui
+++ b/gui/main-window.ui
@@ -236,7 +236,7 @@
</widget>
</item>
<item>
- <widget class="GLWidget" name="pose_display" native="true">
+ <widget class="pose_widget" name="pose_display" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -1353,7 +1353,7 @@
</widget>
<customwidgets>
<customwidget>
- <class>GLWidget</class>
+ <class>pose_widget</class>
<extends>QWidget</extends>
<header>pose-widget/glwidget.h</header>
</customwidget>
diff --git a/pose-widget/glwidget.cpp b/pose-widget/glwidget.cpp
index 17f4e1a2..beddc5e9 100644
--- a/pose-widget/glwidget.cpp
+++ b/pose-widget/glwidget.cpp
@@ -7,6 +7,7 @@
#include "glwidget.h"
#include "compat/util.hpp"
+#include "compat/timer.hpp"
#include <cmath>
#include <algorithm>
#include <QPainter>
@@ -14,44 +15,94 @@
#include <QDebug>
-using namespace euler;
using namespace pose_widget_impl;
-GLWidget::GLWidget(QWidget *parent) : QWidget(parent)
+pose_transform::pose_transform(QWidget* dst) :
+ dst(dst),
+ image(w, h, QImage::Format_ARGB32),
+ image2(w, h, QImage::Format_ARGB32),
+ width(w), height(h)
{
front = QImage(QString(":/images/side1.png"));
back = QImage(QString(":/images/side6.png"));
- rotateBy(0, 0, 0, 0, 0, 0);
+
+ image.fill(Qt::transparent);
+ image2.fill(Qt::transparent);
+
+ rotateBy(0, 0, 0, 0, 0, 0, QSize(w, h));
+
+ project_quad_texture();
+
+ start();
}
-GLWidget::~GLWidget()
+pose_transform::~pose_transform()
{
+ requestInterruption();
+ wait();
}
-void GLWidget::paintEvent(QPaintEvent * event)
+void pose_widget::paintEvent(QPaintEvent* event)
{
QPainter p(this);
- project_quad_texture();
- p.drawImage(event->rect(), image);
+ xform.with_image_lock([&](const QImage& image) {
+ p.drawImage(event->rect(), image);
+ });
+}
+
+void pose_transform::run()
+{
+ for (;;)
+ {
+ if (isInterruptionRequested())
+ break;
+
+ static struct cruft
+ {
+ void lock() {}
+ void unlock() {}
+ } not_a_mutex;
+
+ const cv_status st = cvar.wait_for(not_a_mutex, std::chrono::milliseconds(2000));
+ if (st == cv_status::timeout)
+ continue;
+
+ project_quad_texture();
+ }
+}
+
+pose_widget::pose_widget(QWidget* parent) : QWidget(parent), xform(this)
+{
+}
+
+pose_widget::~pose_widget()
+{
}
-void GLWidget::rotateBy(double xAngle, double yAngle, double zAngle, double x, double y, double z)
+void pose_widget::rotateBy(double xAngle, double yAngle, double zAngle, double x, double y, double z)
+{
+ xform.rotateBy(xAngle, yAngle, zAngle, x, y, z, size());
+}
+
+void pose_transform::rotateBy(double xAngle, double yAngle, double zAngle, double x, double y, double z, const QSize& size)
{
using std::sin;
using std::cos;
static constexpr double d2r = M_PI / 180;
- translation = vec3(x, y, z);
-
euler::euler_t euler(-zAngle * d2r, xAngle * d2r, -yAngle * d2r);
euler::rmat r = euler::euler_to_rmat(euler);
+ lock_guard l(mtx);
+
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
rotation(i, j) = num(r(i, j));
- update();
+ translation = vec3(x, y, z);
+
+ cvar.notify_one();
}
class Triangle {
@@ -62,7 +113,7 @@ public:
bool barycentric_coords(const vec2& px, vec2& uv, int& i) const;
};
-inline vec3 GLWidget::normal(const vec3& p1, const vec3& p2, const vec3& p3)
+inline vec3 pose_transform::normal(const vec3& p1, const vec3& p2, const vec3& p3)
{
using std::sqrt;
@@ -124,13 +175,15 @@ bool Triangle::barycentric_coords(const vec2& px, vec2& uv, int& i) const
return u >= 0 && v >= 0 && u + v <= 1;
}
-void GLWidget::project_quad_texture()
+void pose_transform::project_quad_texture()
{
num dir;
vec2 pt[4];
- const int sx = width() - 1, sy = height() - 1;
+ const int sx = width - 1, sy = height - 1;
vec2 projected[3];
+ lock_guard l(mtx);
+
{
const int sx_ = (sx - std::max(0, (sx - sy)/2)) * 5/9;
const int sy_ = (sy - std::max(0, (sy - sx)/2)) * 5/9;
@@ -163,12 +216,6 @@ void GLWidget::project_quad_texture()
}
const QImage& tex = dir < 0 ? back : front;
-
- if (image.size() != size())
- image = QImage(QSize(sx, sy), QImage::Format_RGBA8888);
-
- image.fill(palette().color(QPalette::Current, QPalette::Window));
-
const int ow = tex.width(), oh = tex.height();
vec2 origs[2][3] =
@@ -260,19 +307,27 @@ void GLWidget::project_quad_texture()
const int pos = y * dest_pitch + x * dest_depth;
- dest[pos + 0] = (r * ax + r__ * ax_) * ay + (r___ * ax + r_ * ax_) * ay_;
+ dest[pos + 2] = (r * ax + r__ * ax_) * ay + (r___ * ax + r_ * ax_) * ay_;
dest[pos + 1] = (g * ax + g__ * ax_) * ay + (g___ * ax + g_ * ax_) * ay_;
- dest[pos + 2] = (b * ax + b__ * ax_) * ay + (b___ * ax + b_ * ax_) * ay_;
+ dest[pos + 0] = (b * ax + b__ * ax_) * ay + (b___ * ax + b_ * ax_) * ay_;
dest[pos + 3] = (a1 * ax + a3 * ax_) * ay + (a4 * ax + a2 * ax_) * ay_;
}
}
+
+ {
+ lock_guard l2(mtx2);
+ image2.fill(Qt::transparent);
+ std::swap(image, image2);
+ }
+
+ run_in_thread_async(dst->thread(), [this]() { dst->update(); });
}
-vec2 GLWidget::project(const vec3 &point)
+vec2 pose_transform::project(const vec3 &point)
{
vec3 ret = rotation * point;
num z = std::max<num>(.75f, 1 + translation.z()/-60);
- num w = width(), h = height();
+ num w = width, h = height;
num x = w * translation.x() / 2 / -40;
if (std::abs(x) > w/2)
x = x > 0 ? w/2 : w/-2;
@@ -282,8 +337,16 @@ vec2 GLWidget::project(const vec3 &point)
return vec2(z * (ret.x() + x), z * (ret.y() + y));
}
-vec3 GLWidget::project2(const vec3 &point)
+vec3 pose_transform::project2(const vec3 &point)
{
return rotation * point;
}
+
+template<typename F>
+inline void pose_transform::with_image_lock(F&& fun)
+{
+ lock_guard l(mtx2);
+
+ fun(image2);
+}
diff --git a/pose-widget/glwidget.h b/pose-widget/glwidget.h
index 66d4289d..4f50008e 100644
--- a/pose-widget/glwidget.h
+++ b/pose-widget/glwidget.h
@@ -9,10 +9,14 @@
#include <QtGlobal>
#include <QWidget>
+#include <QThread>
#include <QPixmap>
#include "api/plugin-api.hpp"
#include "compat/euler.hpp"
+#include <mutex>
+#include <condition_variable>
+
#ifdef BUILD_POSE_WIDGET
# define POSE_WIDGET_EXPORT Q_DECL_EXPORT
#else
@@ -25,31 +29,67 @@ using num = float;
using vec3 = Mat<num, 3, 1>;
using vec2 = Mat<num, 2, 1>;
+using rmat = Mat<num, 3, 3>;
+
using namespace euler;
-class POSE_WIDGET_EXPORT GLWidget : public QWidget
+using lock_guard = std::unique_lock<std::mutex>;
+
+using cv_status = std::cv_status;
+
+class pose_widget;
+
+class pose_transform final : private QThread
{
-public:
- using rmat = Mat<num, 3, 3>;
+ pose_transform(QWidget* dst);
+ ~pose_transform();
+
+ friend class pose_widget;
+
+ void rotateBy(double xAngle, double yAngle, double zAngle,
+ double x, double y, double z,
+ const QSize& size);
+
+ void run() override;
- GLWidget(QWidget *parent);
- ~GLWidget();
- void rotateBy(double xAngle, double yAngle, double zAngle, double x, double y, double z);
-protected:
- void paintEvent(QPaintEvent *event) override;
-private:
vec2 project(const vec3& point);
vec3 project2(const vec3& point);
void project_quad_texture();
+
+ template<typename F>
+ inline void with_image_lock(F&& fun);
+
static vec3 normal(const vec3& p1, const vec3& p2, const vec3& p3);
rmat rotation;
vec3 translation;
- QImage front;
- QImage back;
- QImage image;
+
+ std::condition_variable_any cvar;
+ std::mutex mtx, mtx2;
+
+ QWidget* dst;
+
+ QImage front, back;
+ QImage image, image2;
+
+ int width, height;
+
+ static constexpr int w = 320, h = 240;
+};
+
+class POSE_WIDGET_EXPORT pose_widget final : public QWidget
+{
+public:
+ pose_widget(QWidget *parent = nullptr);
+ ~pose_widget();
+ void rotateBy(double xAngle, double yAngle, double zAngle,
+ double x, double y, double z);
+
+private:
+ pose_transform xform;
+ void paintEvent(QPaintEvent *event) override;
};
}
-using pose_widget_impl::GLWidget;
+using pose_widget_impl::pose_widget;