diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 7f2ad1010..a22370da8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -23,6 +23,7 @@ #include "engine.h" #include "instrument.h" #include "safeReader.h" +#include "workPool.h" #include "../ta-log.h" #include "../fileutils.h" #ifdef HAVE_SDL2 @@ -3123,6 +3124,10 @@ bool DivEngine::switchMaster(bool full) { quitDispatch(); initDispatch(); } + if (renderPool!=NULL) { + delete renderPool; + renderPool=NULL; + } if (initAudioBackend()) { for (int i=0; i2.0f) metroVol=2.0f; + renderPoolThreads=getConfInt("renderPoolThreads",0); if (lowLatency) logI("using low latency mode."); diff --git a/src/engine/engine.h b/src/engine/engine.h index 3a5b1b044..25add2069 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -39,6 +39,8 @@ #include #include +class DivWorkPool; + #define addWarning(x) \ if (warnings.empty()) { \ warnings+=x; \ @@ -485,6 +487,9 @@ class DivEngine { size_t totalProcessed; + unsigned int renderPoolThreads; + DivWorkPool* renderPool; + // MIDI stuff std::function midiCallback=[](const TAMidiMessage&) -> int {return -2;}; @@ -1260,6 +1265,8 @@ class DivEngine { metroAmp(0.0f), metroVol(1.0f), totalProcessed(0), + renderPoolThreads(0), + renderPool(NULL), curOrders(NULL), curPat(NULL), tempIns(NULL), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 270dd7f79..5f06d31aa 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -22,6 +22,7 @@ #define _USE_MATH_DEFINES #include "dispatch.h" #include "engine.h" +#include "workPool.h" #include "../ta-log.h" #include @@ -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(); + if (renderPool==NULL) { + renderPool=new DivWorkPool(renderPoolThreads); + } + // process MIDI events (TODO: everything) if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) { 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 if (cyclespush([this,size](void* d) { + DivDispatchContainer* dc=(DivDispatchContainer*)d; + int total=(cycles*dc->runtotal)/(size<acquire(dc->runPos,total); + dc->runLeft-=total; + dc->runPos+=total; + },&disCont[i]); } + renderPool->wait(); runLeftG-=cycles; cycles=0; } else { cycles-=runLeftG; runLeftG=0; for (int i=0; ipush([](void* d) { + DivDispatchContainer* dc=(DivDispatchContainer*)d; + dc->acquire(dc->runPos,dc->runLeft); + dc->runLeft=0; + },&disCont[i]); } + renderPool->wait(); } } } diff --git a/src/engine/workPool.cpp b/src/engine/workPool.cpp index a62f4ff7a..8772dfba3 100644 --- a/src/engine/workPool.cpp +++ b/src/engine/workPool.cpp @@ -21,14 +21,17 @@ #include "../ta-log.h" #include +#include + void* _workThread(void* inst) { ((DivWorkThread*)inst)->run(); return NULL; } void DivWorkThread::run() { - std::unique_lock unique(selfLock); + //std::unique_lock unique(selfLock); DivPendingTask task; + bool setFuckingPromise=false; logV("running work thread"); @@ -37,11 +40,20 @@ void DivWorkThread::run() { if (tasks.empty()) { lock.unlock(); isBusy=false; - parent->notify.notify_one(); + if (setFuckingPromise) { + parent->notify.set_value(); + setFuckingPromise=false; + std::this_thread::yield(); + } if (terminate) { break; } - notify.wait(unique); + std::future future=notify.get_future(); + future.wait(); + lock.lock(); + notify=std::promise(); + promiseAlreadySet=false; + lock.unlock(); continue; } else { task=tasks.front(); @@ -50,8 +62,13 @@ void DivWorkThread::run() { task.func(task.funcArg); - parent->busyCount--; - parent->notify.notify_one(); + int busyCount=--parent->busyCount; + if (busyCount<0) { + logE("oh no PROBLEM..."); + } + if (busyCount==0) { + setFuckingPromise=true; + } } } } @@ -80,8 +97,8 @@ bool DivWorkThread::busy() { void DivWorkThread::finish() { lock.lock(); terminate=true; + notify.set_value(); lock.unlock(); - notify.notify_one(); thread->join(); } @@ -117,19 +134,38 @@ bool DivWorkPool::busy() { void DivWorkPool::wait() { if (!threaded) return; - std::unique_lock unique(selfLock); + + if (busyCount==0) { + return; + } + + std::future future=notify.get_future(); // start running for (unsigned int i=0; i(); + //SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","YES",NULL); + + pos=0; } DivWorkPool::DivWorkPool(unsigned int threads): diff --git a/src/engine/workPool.h b/src/engine/workPool.h index f47711353..ef430f1ce 100644 --- a/src/engine/workPool.h +++ b/src/engine/workPool.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "fixedQueue.h" @@ -44,12 +44,12 @@ struct DivPendingTask { struct DivWorkThread { DivWorkPool* parent; std::mutex lock; - std::mutex selfLock; std::thread* thread; - std::condition_variable notify; + std::promise notify; FixedQueue tasks; std::atomic isBusy; bool terminate; + bool promiseAlreadySet; void run(); bool assign(const std::function& what, void* arg); @@ -61,7 +61,8 @@ struct DivWorkThread { DivWorkThread(): parent(NULL), isBusy(false), - terminate(false) {} + terminate(false), + promiseAlreadySet(false) {} }; /** @@ -70,12 +71,11 @@ struct DivWorkThread { */ class DivWorkPool { bool threaded; - std::mutex selfLock; unsigned int count; unsigned int pos; DivWorkThread* workThreads; public: - std::condition_variable notify; + std::promise notify; std::atomic busyCount; /** @@ -98,4 +98,4 @@ class DivWorkPool { ~DivWorkPool(); }; -#endif \ No newline at end of file +#endif diff --git a/src/gui/gui.h b/src/gui/gui.h index 280f9debf..37b58cd78 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1576,6 +1576,7 @@ class FurnaceGUI { int classicChipOptions; int wasapiEx; int chanOscThreads; + int renderPoolThreads; unsigned int maxUndoSteps; String mainFontPath; String headFontPath; @@ -1753,6 +1754,7 @@ class FurnaceGUI { classicChipOptions(0), wasapiEx(0), chanOscThreads(0), + renderPoolThreads(0), maxUndoSteps(100), mainFontPath(""), headFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index aebc14e1f..f77200f47 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -892,6 +892,36 @@ void FurnaceGUI::drawSettings() { 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; if (ImGui::Checkbox("Low-latency mode",&lowLatencyB)) { settings.lowLatency=lowLatencyB; @@ -3286,6 +3316,7 @@ void FurnaceGUI::syncSettings() { settings.classicChipOptions=e->getConfInt("classicChipOptions",0); settings.wasapiEx=e->getConfInt("wasapiEx",0); settings.chanOscThreads=e->getConfInt("chanOscThreads",0); + settings.renderPoolThreads=e->getConfInt("renderPoolThreads",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.headFontSize,2,96); @@ -3436,6 +3467,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.classicChipOptions,0,1); clampSetting(settings.wasapiEx,0,1); clampSetting(settings.chanOscThreads,0,256); + clampSetting(settings.renderPoolThreads,0,256); if (settings.exportLoops<0.0) settings.exportLoops=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("wasapiEx",settings.wasapiEx); e->setConf("chanOscThreads",settings.chanOscThreads); + e->setConf("renderPoolThreads",settings.renderPoolThreads); // colors for (int i=0; i