aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2020-08-14 18:53:08 +0200
committerJulian T <julian@jtle.dk>2020-08-14 18:53:08 +0200
commit5ca04e5b2ec8eef88df6cbd4e3d09ac7dab55c0d (patch)
tree8333b03fc7f1d32be0fee1d9230899b6268cc8ae
parentbef819f1e2a3d998d7ec7968dbb5fb691fc0d9f8 (diff)
Added multithreading
-rw-r--r--app/mainwindow.cpp7
-rw-r--r--app/rendercoord.cpp127
-rw-r--r--app/rendercoord.hpp27
3 files changed, 141 insertions, 20 deletions
diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp
index a365f38..3fcc9c5 100644
--- a/app/mainwindow.cpp
+++ b/app/mainwindow.cpp
@@ -18,9 +18,16 @@ MainWindow::MainWindow(Renderer r)
saveAct->setStatusTip(tr("Save the rendered image"));
connect(saveAct, &QAction::triggered, this, &MainWindow::saveimage);
+ auto stopAct = new QAction(tr("&Stop"), this);
+ stopAct->setStatusTip(tr("Stop and sync threads"));
+ QObject::connect(stopAct, &QAction::triggered, &m_render, &RenderCoordinator::stop);
+
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(saveAct);
+ fileMenu = menuBar()->addMenu(tr("&Render"));
+ fileMenu->addAction(stopAct);
+
helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt);
diff --git a/app/rendercoord.cpp b/app/rendercoord.cpp
index a5a3da5..fd325e3 100644
--- a/app/rendercoord.cpp
+++ b/app/rendercoord.cpp
@@ -1,8 +1,10 @@
#include "rendercoord.hpp"
+#include <algorithm>
#include <qobject.h>
#include <iostream>
#include <qrgb.h>
+#include <qsemaphore.h>
#include <render.hpp>
#include <sstream>
@@ -16,12 +18,14 @@ uint32_t colorToUint32(const Color &c) {
}
// Run by main thread
-RenderThread::RenderThread(Renderer r, QObject *parent, unsigned id)
+RenderThread::RenderThread(Renderer r, unsigned threads, QObject *parent, unsigned id)
: QThread(parent),
m_lock(1),
- m_render(r)
+ m_render(r),
+ m_pause(1)
{
m_id = id;
+ m_workers = threads;
}
// Run on new thread
@@ -35,17 +39,21 @@ void RenderThread::run() {
m_current_samples = 0;
- for (unsigned sample = 1; sample < m_samples+1; sample++) {
- for (unsigned x = 0; x < m_render.m_width; x++) {
- for (unsigned y = 0; y < m_render.m_height; y++) {
- auto index = x + y * m_render.m_height;
+ for (int sample = 0; sample < m_samples; sample++) {
+ m_current_samples = sample;
+ // Probably not that smart
+ m_pause.acquire();
+ m_pause.release();
+
+ for (unsigned y = m_id; y < m_render.m_height; y += m_workers) {
+ for (unsigned x = 0; x < m_render.m_width; x++) {
+ auto index = x + y * m_render.m_width;
sum[index] += m_render.render(m_render.m_width - x, m_render.m_height - y, 1);
- m_writebuffer[index] = colorToUint32(sum[index] / sample);
+ m_writebuffer[index] = colorToUint32(sum[index] / (sample+1));
}
}
- m_current_samples = sample;
}
// Signal done
@@ -54,6 +62,14 @@ void RenderThread::run() {
}
}
+void RenderThread::pause() {
+ m_pause.acquire();
+}
+
+void RenderThread::resume() {
+ m_pause.release();
+}
+
int RenderThread::render(QRgb *buffer, unsigned samples) {
// Check if already running
if (!m_lock.tryAcquire()) {
@@ -66,6 +82,15 @@ int RenderThread::render(QRgb *buffer, unsigned samples) {
return 0;
}
+unsigned RenderThread::stop() {
+ stopAt(m_current_samples);
+ return m_current_samples;
+}
+
+void RenderThread::stopAt(int at) {
+ m_samples = at;
+}
+
// Running on main thread
unsigned RenderThread::current_samples() {
// No sync should not be a problem here.
@@ -76,17 +101,30 @@ RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Render
: QObject(parent),
m_target(target),
m_renderer(r),
- m_worker(m_renderer, this),
m_timer(this)
{
m_status = status;
- m_worker.start();
+ // Create and start workers
+ for (int i = 0; i < 4; i++) {
+ auto thread = new RenderThread(m_renderer, 4, this, i);
+
+ thread->start();
+ QObject::connect(thread, &RenderThread::done, this, &RenderCoordinator::workerDone);
+
+ m_workers.push_back(thread);
+ }
+
+ render();
- QObject::connect(&m_worker, &RenderThread::done,
- this, &RenderCoordinator::workerDone);
+}
- m_worker.render(target.m_drawbuffer, 100);
+void RenderCoordinator::render() {
+ m_started = 0;
+ for (auto thd : m_workers) {
+ thd->render(m_target.m_drawbuffer, 20);
+ m_started++;
+ }
m_state = running;
updateUi();
@@ -94,15 +132,66 @@ RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Render
QObject::connect(&m_timer, &QTimer::timeout, this, &RenderCoordinator::updateUi);
m_timer.start(500);
+}
+
+void RenderCoordinator::stop() {
+ unsigned max = 0;
+ for (auto thd : m_workers) {
+ thd->pause();
+ auto val = thd->current_samples();
+ if (val>max) {
+ max = val;
+ }
+ }
+
+ std::cout << max << std::endl;
+
+ for (auto thd : m_workers) {
+ thd->stopAt(max+1);
+ thd->resume();
+ }
+
+ m_state = stopping;
+ updateUi();
}
void RenderCoordinator::workerDone(unsigned workerid) {
+ std::cout << "Worker " << workerid << " done!" << std::endl;
+ if (--m_started) {
+ return;
+ }
+ std::cout << "All done :-)" << std::endl;
+
+ // All workers are done
m_state = stopped;
m_timer.stop();
updateUi();
}
+unsigned RenderCoordinator::calcStats(unsigned *max, unsigned *min, double *avg) {
+ unsigned count = 0;
+ unsigned sum = 0;
+ for (auto thd : m_workers) {
+ auto val = thd->current_samples();
+ if (min && (val < *min || !count)) {
+ *min = val;
+ }
+ if (max && (val > *max || !count)) {
+ *max = val;
+ }
+
+ sum += val;
+ count++;
+ }
+
+ if (avg) {
+ *avg = (double)sum / count;
+ }
+
+ return count;
+}
+
void RenderCoordinator::updateUi() {
m_target.repaint();
@@ -111,8 +200,18 @@ void RenderCoordinator::updateUi() {
return;
}
+ // Gather statictics from workers
+ unsigned max;
+ unsigned min;
+ double avg;
+ unsigned count = calcStats(&max, &min, &avg);
+
std::ostringstream status;
- status << states[m_state] << " " << m_worker.current_samples() << " samples";
+ status << states[m_state] <<
+ " Threads: " << count <<
+ " Max: " << max << " samples" <<
+ " Min: " << min << " samples" <<
+ " Avg: " << avg << " samples";
m_status->setText(QString::fromStdString(status.str()));
}
diff --git a/app/rendercoord.hpp b/app/rendercoord.hpp
index 113b4dc..a749f8a 100644
--- a/app/rendercoord.hpp
+++ b/app/rendercoord.hpp
@@ -2,6 +2,7 @@
#define RENDER_THREAD_H
#include "draw.hpp"
+#include <atomic>
#include <qlabel.h>
#include <render.hpp>
@@ -16,10 +17,17 @@ class RenderThread : public QThread {
Q_OBJECT
public:
- RenderThread(Renderer r, QObject *parent = nullptr, unsigned id = 0);
+ RenderThread(Renderer r, unsigned threads, QObject *parent = nullptr, unsigned id = 0);
// Returns 0 if successful or 1 if busy
int render(QRgb *buffer, unsigned samples);
+
+ void pause();
+ void resume();
+
+ unsigned stop();
+ void stopAt(int at);
+
unsigned current_samples();
signals:
@@ -31,18 +39,21 @@ class RenderThread : public QThread {
QSemaphore m_lock;
QRgb *m_writebuffer;
- unsigned m_samples;
- unsigned m_current_samples;
+ std::atomic_int m_samples;
+ std::atomic_int m_current_samples;
Renderer m_render;
+ unsigned m_workers;
+
// Value in here means work is to be done
QSemaphore m_work;
+ QSemaphore m_pause;
unsigned m_id;
};
-const std::string states[] = { "Stopped", "Running" };
-enum State { stopped, running };
+const std::string states[] = { "Stopped", "Running", "Stopping" };
+enum State { stopped, running, stopping };
class RenderCoordinator : public QObject {
Q_OBJECT
@@ -54,15 +65,19 @@ class RenderCoordinator : public QObject {
public slots:
void workerDone(unsigned workerid);
+ void stop();
private slots:
void updateUi();
private:
+ unsigned calcStats(unsigned *max, unsigned *min, double *avg);
+
DrawWidget &m_target;
Renderer m_renderer;
- RenderThread m_worker;
+ std::vector<RenderThread*> m_workers;
+ unsigned m_started;
QLabel *m_status;
QTimer m_timer;