initial multithreaded rendering support
parent
fc93dc0519
commit
2a8015c406
19
notes
19
notes
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue