initial multithreaded rendering support

portability/boost
zetaPRIME 2019-01-10 03:33:33 -05:00
parent fc93dc0519
commit 2a8015c406
6 changed files with 3555 additions and 17 deletions

19
notes
View File

@ -45,27 +45,16 @@ project data {
TODO {
immediate frontburner {
- transpose selection (alt+up/down for semitone/0x01, left+right for octave/0x10)
- pattern cut+copy+paste
- ^ channel-column splitting (partial channel copy/paste)
- pattern properties dialog (name, length, time signature)
- probably move the "process all nodes" part of tick processing into its own function?
- set default suffix on save-as dialog
- have the graph scene actually keep track of keys-to-notes on preview (like pattern editor)
multithreaded audio
^ audio engine invokes workers, then QThread::wait()s on them
# fix how qt5.12 broke header text (removed elide for now)
- multithreaded audio
- ^ audio engine invokes workers, then QThread::wait()s on them
figure out weirdness with things missing commands in multithreaded mode(!?)
give node helper functions to move ports (index-wise) and collapse them (eliminate numbering holes)
node event that fires on port connect/disconnect (to enable, say, automatic collapsing, and changing of behavior by how many connections there are)
}
- make short connections look less wonky
# fix how qt5.12 broke header text (removed elide for now)
misc features needed before proper release {
song metadata (title, artist, comment, default bpm)

View File

@ -46,6 +46,17 @@ void AudioEngine::postInit() {
chTrack.reserve(256);
noteEndQueue.reserve(256);
nameTrack.reserve(64+1); // +1 to make extra sure it doesn't rehash later
if (auto cores = QThread::idealThreadCount(); cores > 1) {
workers.reserve(static_cast<size_t>(cores));
for (int i = 0; i < cores; i++) {
auto wk = new AudioWorker();
wk->thread = new QThread(this);
wk->moveToThread(wk->thread);
connect(wk->thread, &QThread::started, wk, &AudioWorker::mainLoop);
workers.push_back(wk);
}
}
}
void* AudioEngine::tickAlloc(size_t size) {
@ -207,6 +218,8 @@ void AudioEngine::buildQueue() {
qCurrent->clear();
std::swap(qCurrent, qNext);
}
// set queue size
wqueue = moodycamel::ConcurrentQueue<std::shared_ptr<Node>>(queue.size());
queueValid = true;
}
@ -474,5 +487,37 @@ void AudioEngine::nextTick() {
}
void AudioEngine::processNodes() {
for (auto n : queue) if (!n->try_process()) qWarning() << "Dependency check failed in single threaded mode!";
if (workers.empty()) {
for (auto n : queue) if (!n->try_process()) qWarning() << "Dependency check failed in single threaded mode!";
} else {
for (auto n : queue) wqueue.enqueue(n);
for (auto w : workers) {
w->thread->start();//w->running.store(true);
w->thread->setPriority(QThread::TimeCriticalPriority);
}
while (true) {
QThread::yieldCurrentThread();
//bool b = false;
for (auto w : workers) w->thread->wait();//if (w->running.load()) { b = true; break; }
//if (b) continue;
break;
}
}
}
AudioWorker::AudioWorker(QObject* parent) : QObject(parent) {
}
void AudioWorker::mainLoop() {
auto& queue = audioEngine->wqueue;
// process
std::shared_ptr<Node> n;
while (queue.try_dequeue(n)) {
if (!n) continue;
while (!n->try_process(true)) QThread::yieldCurrentThread();
}
thread->exit();
}

View File

@ -10,12 +10,15 @@
#include <QIODevice>
#include <QAudioOutput>
#include "inclib/concurrentqueue.h"
class QThread;
namespace Xybrid::Data {
class Project;
class Node;
}
namespace Xybrid::Audio {
class AudioWorker;
template <typename T> struct PointerCompare {
bool operator()(T* a, T* b) const { return *a == *b; }
size_t operator()(T* a) const { return std::hash<T>()(*a); }
@ -28,6 +31,7 @@ namespace Xybrid::Audio {
NoteInfo(uint8_t p, uint16_t nId) { valid = true; port = p; noteId = nId; }
};
class AudioEngine : public QIODevice {
friend class AudioWorker;
Q_OBJECT
explicit AudioEngine(QObject *parent = nullptr);
public:
@ -55,6 +59,9 @@ namespace Xybrid::Audio {
size_t tickId = 0;
std::shared_ptr<Data::Project> project;
std::vector<AudioWorker*> workers;
moodycamel::ConcurrentQueue<std::shared_ptr<Data::Node>> wqueue;
std::deque<std::shared_ptr<Data::Node>> queue;
bool queueValid;
void buildQueue();
@ -108,5 +115,15 @@ namespace Xybrid::Audio {
public slots:
};
class AudioWorker : public QObject {
friend class AudioEngine;
Q_OBJECT
explicit AudioWorker(QObject* parent = nullptr);
QThread* thread;
void mainLoop();
};
extern AudioEngine* audioEngine;
}

View File

@ -23,6 +23,7 @@ namespace Xybrid::Config {
namespace Xybrid::Audio {
class AudioEngine;
class AudioWorker;
}
namespace Xybrid::Data {
@ -69,6 +70,7 @@ namespace Xybrid::Data {
class Node : public std::enable_shared_from_this<Node> {
friend class Audio::AudioEngine;
friend class Audio::AudioWorker;
size_t tick_last = 0;
bool try_process(bool checkDependencies = true);
public:

File diff suppressed because it is too large Load Diff

View File

@ -92,7 +92,8 @@ HEADERS += \
ui/gadgets/knobgadget.h \
gadgets/gainbalance.h \
util/pattern.h \
gadgets/transpose.h
gadgets/transpose.h \
inclib/concurrentqueue.h
FORMS += \
mainwindow.ui