Merge branch 'threaded' - but will be hidden

under a cheat code for now
This commit is contained in:
tildearrow 2023-09-06 19:27:55 -05:00
commit c1b6aed04b
7 changed files with 124 additions and 27 deletions

View file

@ -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.");

View file

@ -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),

View file

@ -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();
} }
} }
} }

View file

@ -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();
// wait workThreads[i].promiseAlreadySet=true;
while (busyCount!=0) { workThreads[i].notify.set_value();
if (notify.wait_for(unique,std::chrono::milliseconds(100))==std::cv_status::timeout) { workThreads[i].lock.unlock();
logW("DivWorkPool: wait() timed out!"); } catch (std::exception& e) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"EXCEPTION ON WAIT",e.what(),NULL);
abort();
}
} }
} }
std::this_thread::yield();
// wait
//SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","waiting on future.",NULL);
future.wait();
//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):

View file

@ -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;
/** /**
@ -98,4 +98,4 @@ class DivWorkPool {
~DivWorkPool(); ~DivWorkPool();
}; };
#endif #endif

View file

@ -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(""),

View file

@ -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++) {