diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2017-03-29 19:28:20 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2017-03-29 19:28:27 +0200 |
commit | 87c02a5399abb0dfe020f3cd1c5e2337c0d8cb8f (patch) | |
tree | ed82c1509ca562b49aee7df440ce575ceb3beb88 /pose-widget | |
parent | 94f510f31c3f5ca79f24f2daa085cb34d5650565 (diff) |
pose-widget: reduce latency
- project on a separate thread
- use Qt's native ARGB32
- use double buffering
- more fine-grained locks
Diffstat (limited to 'pose-widget')
-rw-r--r-- | pose-widget/glwidget.cpp | 113 | ||||
-rw-r--r-- | pose-widget/glwidget.h | 66 |
2 files changed, 141 insertions, 38 deletions
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; |