Merge branch 'threaded' - but will be hidden
under a cheat code for now
This commit is contained in:
commit
c1b6aed04b
|
@ -23,6 +23,7 @@
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "instrument.h"
|
#include "instrument.h"
|
||||||
#include "safeReader.h"
|
#include "safeReader.h"
|
||||||
|
#include "workPool.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include "../fileutils.h"
|
#include "../fileutils.h"
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
|
@ -3123,6 +3124,10 @@ bool DivEngine::switchMaster(bool full) {
|
||||||
quitDispatch();
|
quitDispatch();
|
||||||
initDispatch();
|
initDispatch();
|
||||||
}
|
}
|
||||||
|
if (renderPool!=NULL) {
|
||||||
|
delete renderPool;
|
||||||
|
renderPool=NULL;
|
||||||
|
}
|
||||||
if (initAudioBackend()) {
|
if (initAudioBackend()) {
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
disCont[i].setRates(got.rate);
|
disCont[i].setRates(got.rate);
|
||||||
|
@ -3314,6 +3319,7 @@ bool DivEngine::initAudioBackend() {
|
||||||
midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE);
|
midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE);
|
||||||
if (metroVol<0.0f) metroVol=0.0f;
|
if (metroVol<0.0f) metroVol=0.0f;
|
||||||
if (metroVol>2.0f) metroVol=2.0f;
|
if (metroVol>2.0f) metroVol=2.0f;
|
||||||
|
renderPoolThreads=getConfInt("renderPoolThreads",0);
|
||||||
|
|
||||||
if (lowLatency) logI("using low latency mode.");
|
if (lowLatency) logI("using low latency mode.");
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
|
class DivWorkPool;
|
||||||
|
|
||||||
#define addWarning(x) \
|
#define addWarning(x) \
|
||||||
if (warnings.empty()) { \
|
if (warnings.empty()) { \
|
||||||
warnings+=x; \
|
warnings+=x; \
|
||||||
|
@ -485,6 +487,9 @@ class DivEngine {
|
||||||
|
|
||||||
size_t totalProcessed;
|
size_t totalProcessed;
|
||||||
|
|
||||||
|
unsigned int renderPoolThreads;
|
||||||
|
DivWorkPool* renderPool;
|
||||||
|
|
||||||
// MIDI stuff
|
// MIDI stuff
|
||||||
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -2;};
|
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -2;};
|
||||||
|
|
||||||
|
@ -1260,6 +1265,8 @@ class DivEngine {
|
||||||
metroAmp(0.0f),
|
metroAmp(0.0f),
|
||||||
metroVol(1.0f),
|
metroVol(1.0f),
|
||||||
totalProcessed(0),
|
totalProcessed(0),
|
||||||
|
renderPoolThreads(0),
|
||||||
|
renderPool(NULL),
|
||||||
curOrders(NULL),
|
curOrders(NULL),
|
||||||
curPat(NULL),
|
curPat(NULL),
|
||||||
tempIns(NULL),
|
tempIns(NULL),
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include "dispatch.h"
|
#include "dispatch.h"
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
#include "workPool.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
@ -1788,6 +1789,10 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now();
|
std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
if (renderPool==NULL) {
|
||||||
|
renderPool=new DivWorkPool(renderPoolThreads);
|
||||||
|
}
|
||||||
|
|
||||||
// process MIDI events (TODO: everything)
|
// process MIDI events (TODO: everything)
|
||||||
if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) {
|
if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) {
|
||||||
TAMidiMessage& msg=output->midiIn->queue.front();
|
TAMidiMessage& msg=output->midiIn->queue.front();
|
||||||
|
@ -2061,20 +2066,28 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
// 5. tick the clock and fill buffers as needed
|
// 5. tick the clock and fill buffers as needed
|
||||||
if (cycles<runLeftG) {
|
if (cycles<runLeftG) {
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
int total=(cycles*disCont[i].runtotal)/(size<<MASTER_CLOCK_PREC);
|
renderPool->push([this,size](void* d) {
|
||||||
disCont[i].acquire(disCont[i].runPos,total);
|
DivDispatchContainer* dc=(DivDispatchContainer*)d;
|
||||||
disCont[i].runLeft-=total;
|
int total=(cycles*dc->runtotal)/(size<<MASTER_CLOCK_PREC);
|
||||||
disCont[i].runPos+=total;
|
dc->acquire(dc->runPos,total);
|
||||||
|
dc->runLeft-=total;
|
||||||
|
dc->runPos+=total;
|
||||||
|
},&disCont[i]);
|
||||||
}
|
}
|
||||||
|
renderPool->wait();
|
||||||
runLeftG-=cycles;
|
runLeftG-=cycles;
|
||||||
cycles=0;
|
cycles=0;
|
||||||
} else {
|
} else {
|
||||||
cycles-=runLeftG;
|
cycles-=runLeftG;
|
||||||
runLeftG=0;
|
runLeftG=0;
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
disCont[i].acquire(disCont[i].runPos,disCont[i].runLeft);
|
renderPool->push([](void* d) {
|
||||||
disCont[i].runLeft=0;
|
DivDispatchContainer* dc=(DivDispatchContainer*)d;
|
||||||
|
dc->acquire(dc->runPos,dc->runLeft);
|
||||||
|
dc->runLeft=0;
|
||||||
|
},&disCont[i]);
|
||||||
}
|
}
|
||||||
|
renderPool->wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,17 @@
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
void* _workThread(void* inst) {
|
void* _workThread(void* inst) {
|
||||||
((DivWorkThread*)inst)->run();
|
((DivWorkThread*)inst)->run();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivWorkThread::run() {
|
void DivWorkThread::run() {
|
||||||
std::unique_lock<std::mutex> unique(selfLock);
|
//std::unique_lock<std::mutex> unique(selfLock);
|
||||||
DivPendingTask task;
|
DivPendingTask task;
|
||||||
|
bool setFuckingPromise=false;
|
||||||
|
|
||||||
logV("running work thread");
|
logV("running work thread");
|
||||||
|
|
||||||
|
@ -37,11 +40,20 @@ void DivWorkThread::run() {
|
||||||
if (tasks.empty()) {
|
if (tasks.empty()) {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
isBusy=false;
|
isBusy=false;
|
||||||
parent->notify.notify_one();
|
if (setFuckingPromise) {
|
||||||
|
parent->notify.set_value();
|
||||||
|
setFuckingPromise=false;
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
if (terminate) {
|
if (terminate) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
notify.wait(unique);
|
std::future<void> future=notify.get_future();
|
||||||
|
future.wait();
|
||||||
|
lock.lock();
|
||||||
|
notify=std::promise<void>();
|
||||||
|
promiseAlreadySet=false;
|
||||||
|
lock.unlock();
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
task=tasks.front();
|
task=tasks.front();
|
||||||
|
@ -50,8 +62,13 @@ void DivWorkThread::run() {
|
||||||
|
|
||||||
task.func(task.funcArg);
|
task.func(task.funcArg);
|
||||||
|
|
||||||
parent->busyCount--;
|
int busyCount=--parent->busyCount;
|
||||||
parent->notify.notify_one();
|
if (busyCount<0) {
|
||||||
|
logE("oh no PROBLEM...");
|
||||||
|
}
|
||||||
|
if (busyCount==0) {
|
||||||
|
setFuckingPromise=true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,8 +97,8 @@ bool DivWorkThread::busy() {
|
||||||
void DivWorkThread::finish() {
|
void DivWorkThread::finish() {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
terminate=true;
|
terminate=true;
|
||||||
|
notify.set_value();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
notify.notify_one();
|
|
||||||
thread->join();
|
thread->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,19 +134,38 @@ bool DivWorkPool::busy() {
|
||||||
|
|
||||||
void DivWorkPool::wait() {
|
void DivWorkPool::wait() {
|
||||||
if (!threaded) return;
|
if (!threaded) return;
|
||||||
std::unique_lock<std::mutex> unique(selfLock);
|
|
||||||
|
if (busyCount==0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> future=notify.get_future();
|
||||||
|
|
||||||
// start running
|
// start running
|
||||||
for (unsigned int i=0; i<count; i++) {
|
for (unsigned int i=0; i<count; i++) {
|
||||||
workThreads[i].notify.notify_one();
|
if (!workThreads[i].promiseAlreadySet) {
|
||||||
|
try {
|
||||||
|
workThreads[i].lock.lock();
|
||||||
|
workThreads[i].promiseAlreadySet=true;
|
||||||
|
workThreads[i].notify.set_value();
|
||||||
|
workThreads[i].lock.unlock();
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"EXCEPTION ON WAIT",e.what(),NULL);
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::this_thread::yield();
|
||||||
|
|
||||||
// wait
|
// wait
|
||||||
while (busyCount!=0) {
|
//SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","waiting on future.",NULL);
|
||||||
if (notify.wait_for(unique,std::chrono::milliseconds(100))==std::cv_status::timeout) {
|
future.wait();
|
||||||
logW("DivWorkPool: wait() timed out!");
|
//SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","waited - reset promise.",NULL);
|
||||||
}
|
|
||||||
}
|
notify=std::promise<void>();
|
||||||
|
//SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","YES",NULL);
|
||||||
|
|
||||||
|
pos=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DivWorkPool::DivWorkPool(unsigned int threads):
|
DivWorkPool::DivWorkPool(unsigned int threads):
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <condition_variable>
|
#include <future>
|
||||||
|
|
||||||
#include "fixedQueue.h"
|
#include "fixedQueue.h"
|
||||||
|
|
||||||
|
@ -44,12 +44,12 @@ struct DivPendingTask {
|
||||||
struct DivWorkThread {
|
struct DivWorkThread {
|
||||||
DivWorkPool* parent;
|
DivWorkPool* parent;
|
||||||
std::mutex lock;
|
std::mutex lock;
|
||||||
std::mutex selfLock;
|
|
||||||
std::thread* thread;
|
std::thread* thread;
|
||||||
std::condition_variable notify;
|
std::promise<void> notify;
|
||||||
FixedQueue<DivPendingTask,32> tasks;
|
FixedQueue<DivPendingTask,32> tasks;
|
||||||
std::atomic<bool> isBusy;
|
std::atomic<bool> isBusy;
|
||||||
bool terminate;
|
bool terminate;
|
||||||
|
bool promiseAlreadySet;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
bool assign(const std::function<void(void*)>& what, void* arg);
|
bool assign(const std::function<void(void*)>& what, void* arg);
|
||||||
|
@ -61,7 +61,8 @@ struct DivWorkThread {
|
||||||
DivWorkThread():
|
DivWorkThread():
|
||||||
parent(NULL),
|
parent(NULL),
|
||||||
isBusy(false),
|
isBusy(false),
|
||||||
terminate(false) {}
|
terminate(false),
|
||||||
|
promiseAlreadySet(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,12 +71,11 @@ struct DivWorkThread {
|
||||||
*/
|
*/
|
||||||
class DivWorkPool {
|
class DivWorkPool {
|
||||||
bool threaded;
|
bool threaded;
|
||||||
std::mutex selfLock;
|
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
unsigned int pos;
|
unsigned int pos;
|
||||||
DivWorkThread* workThreads;
|
DivWorkThread* workThreads;
|
||||||
public:
|
public:
|
||||||
std::condition_variable notify;
|
std::promise<void> notify;
|
||||||
std::atomic<int> busyCount;
|
std::atomic<int> busyCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1576,6 +1576,7 @@ class FurnaceGUI {
|
||||||
int classicChipOptions;
|
int classicChipOptions;
|
||||||
int wasapiEx;
|
int wasapiEx;
|
||||||
int chanOscThreads;
|
int chanOscThreads;
|
||||||
|
int renderPoolThreads;
|
||||||
unsigned int maxUndoSteps;
|
unsigned int maxUndoSteps;
|
||||||
String mainFontPath;
|
String mainFontPath;
|
||||||
String headFontPath;
|
String headFontPath;
|
||||||
|
@ -1753,6 +1754,7 @@ class FurnaceGUI {
|
||||||
classicChipOptions(0),
|
classicChipOptions(0),
|
||||||
wasapiEx(0),
|
wasapiEx(0),
|
||||||
chanOscThreads(0),
|
chanOscThreads(0),
|
||||||
|
renderPoolThreads(0),
|
||||||
maxUndoSteps(100),
|
maxUndoSteps(100),
|
||||||
mainFontPath(""),
|
mainFontPath(""),
|
||||||
headFontPath(""),
|
headFontPath(""),
|
||||||
|
|
|
@ -892,6 +892,36 @@ void FurnaceGUI::drawSettings() {
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool renderPoolThreadsB=(settings.renderPoolThreads>0);
|
||||||
|
if (ImGui::Checkbox("Multi-threaded (EXPERIMENTAL)",&renderPoolThreadsB)) {
|
||||||
|
if (renderPoolThreadsB) {
|
||||||
|
settings.renderPoolThreads=2;
|
||||||
|
} else {
|
||||||
|
settings.renderPoolThreads=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("runs chip emulation on separate threads.\nmay increase performance on multi-core CPUs.\n\nwarnings:\n- experimental! currently broken/not working well.\n- only useful on multi-chip songs.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderPoolThreadsB) {
|
||||||
|
pushWarningColor(settings.renderPoolThreads>cpuCores,settings.renderPoolThreads>cpuCores);
|
||||||
|
if (ImGui::InputInt("Number of threads",&settings.renderPoolThreads)) {
|
||||||
|
if (settings.renderPoolThreads<2) settings.renderPoolThreads=2;
|
||||||
|
if (settings.renderPoolThreads>32) settings.renderPoolThreads=32;
|
||||||
|
}
|
||||||
|
if (settings.renderPoolThreads>=DIV_MAX_CHIPS) {
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("that's the limit!");
|
||||||
|
}
|
||||||
|
} else if (settings.renderPoolThreads>cpuCores) {
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("it is a VERY bad idea to set this number higher than your CPU core count (%d)!",cpuCores);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popWarningColor();
|
||||||
|
}
|
||||||
|
|
||||||
bool lowLatencyB=settings.lowLatency;
|
bool lowLatencyB=settings.lowLatency;
|
||||||
if (ImGui::Checkbox("Low-latency mode",&lowLatencyB)) {
|
if (ImGui::Checkbox("Low-latency mode",&lowLatencyB)) {
|
||||||
settings.lowLatency=lowLatencyB;
|
settings.lowLatency=lowLatencyB;
|
||||||
|
@ -3286,6 +3316,7 @@ void FurnaceGUI::syncSettings() {
|
||||||
settings.classicChipOptions=e->getConfInt("classicChipOptions",0);
|
settings.classicChipOptions=e->getConfInt("classicChipOptions",0);
|
||||||
settings.wasapiEx=e->getConfInt("wasapiEx",0);
|
settings.wasapiEx=e->getConfInt("wasapiEx",0);
|
||||||
settings.chanOscThreads=e->getConfInt("chanOscThreads",0);
|
settings.chanOscThreads=e->getConfInt("chanOscThreads",0);
|
||||||
|
settings.renderPoolThreads=e->getConfInt("renderPoolThreads",0);
|
||||||
|
|
||||||
clampSetting(settings.mainFontSize,2,96);
|
clampSetting(settings.mainFontSize,2,96);
|
||||||
clampSetting(settings.headFontSize,2,96);
|
clampSetting(settings.headFontSize,2,96);
|
||||||
|
@ -3436,6 +3467,7 @@ void FurnaceGUI::syncSettings() {
|
||||||
clampSetting(settings.classicChipOptions,0,1);
|
clampSetting(settings.classicChipOptions,0,1);
|
||||||
clampSetting(settings.wasapiEx,0,1);
|
clampSetting(settings.wasapiEx,0,1);
|
||||||
clampSetting(settings.chanOscThreads,0,256);
|
clampSetting(settings.chanOscThreads,0,256);
|
||||||
|
clampSetting(settings.renderPoolThreads,0,256);
|
||||||
|
|
||||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||||
|
@ -3693,6 +3725,7 @@ void FurnaceGUI::commitSettings() {
|
||||||
e->setConf("classicChipOptions",settings.classicChipOptions);
|
e->setConf("classicChipOptions",settings.classicChipOptions);
|
||||||
e->setConf("wasapiEx",settings.wasapiEx);
|
e->setConf("wasapiEx",settings.wasapiEx);
|
||||||
e->setConf("chanOscThreads",settings.chanOscThreads);
|
e->setConf("chanOscThreads",settings.chanOscThreads);
|
||||||
|
e->setConf("renderPoolThreads",settings.renderPoolThreads);
|
||||||
|
|
||||||
// colors
|
// colors
|
||||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||||
|
|
Loading…
Reference in a new issue