@@ -37,36 +37,39 @@ struct Module { | |||||
std::vector<Light> lights; | std::vector<Light> lights; | ||||
std::vector<ParamQuantity*> paramQuantities; | std::vector<ParamQuantity*> paramQuantities; | ||||
/** ID of the Module immediately to the left, or -1 if nonexistent. */ | |||||
int leftModuleId = -1; | |||||
/** Pointer to the left Module, or NULL if nonexistent. */ | |||||
Module *leftModule = NULL; | |||||
/** Double buffer for receiving messages from the left adjacent module. | |||||
If this module intends to receive messages from the left, allocate both message buffers with identical blocks of memory (arrays, structs, etc). | |||||
Remember to free the buffer in the Module destructor. | |||||
Example: | |||||
leftProducerMessage = new MyModuleMessage; | |||||
leftConsumerMessage = new MyModuleMessage; | |||||
Modules must check a foreign module's `model` before attempting to write its message buffer. | |||||
Once the module is checked, you can reinterpret_cast its leftProducerMessage for speed. | |||||
Once you write a message, set leftMessageFlipRequested to true, to request that the messages are flipped at the end of the timestep. | |||||
Thus, message-passing has 1-sample latency. | |||||
You may choose for the Module to write to its own message buffer for consumption by other modules, i.e. "pull" rather than "push". | |||||
As long as this convention is followed by the left module, this is fine. | |||||
*/ | |||||
void *leftProducerMessage = NULL; | |||||
void *leftConsumerMessage = NULL; | |||||
bool leftMessageFlipRequested = false; | |||||
int rightModuleId = -1; | |||||
Module *rightModule = NULL; | |||||
void *rightProducerMessage = NULL; | |||||
void *rightConsumerMessage = NULL; | |||||
bool rightMessageFlipRequested = true; | |||||
/** Represents a message-passing channel for an adjacent module. */ | |||||
struct Expander { | |||||
/** ID of the expander module, or -1 if nonexistent. */ | |||||
int moduleId = -1; | |||||
/** Pointer to the expander Module, or NULL if nonexistent. */ | |||||
Module *module = NULL; | |||||
/** Double buffer for receiving messages from the expander module. | |||||
If you intend to receive messages from an expander, allocate both message buffers with identical blocks of memory (arrays, structs, etc). | |||||
Remember to free the buffer in the Module destructor. | |||||
Example: | |||||
rightExpander.producerMessage = new MyExpanderMessage; | |||||
rightExpander.consumerMessage = new MyExpanderMessage; | |||||
You must check the expander module's `model` before attempting to write its message buffer. | |||||
Once the module is checked, you can reinterpret_cast its producerMessage at no performance cost. | |||||
Producer messages are intended to be write-only. | |||||
Consumer messages are intended to be read-only. | |||||
Once you write a message, set messageFlipRequested to true to request that the messages are flipped at the end of the timestep. | |||||
This means that message-passing has 1-sample latency. | |||||
You may choose for your Module to instead write to its own message buffer for consumption by other modules, i.e. the expander "pulls" rather than this module "pushing". | |||||
As long as this convention is followed by the other module, this is fine. | |||||
*/ | |||||
void *producerMessage = NULL; | |||||
void *consumerMessage = NULL; | |||||
bool messageFlipRequested = false; | |||||
}; | |||||
Expander leftExpander; | |||||
Expander rightExpander; | |||||
/** Seconds spent in the process() method, with exponential smoothing. | /** Seconds spent in the process() method, with exponential smoothing. | ||||
Only written when CPU timing is enabled, since time measurement is expensive. | Only written when CPU timing is enabled, since time measurement is expensive. | ||||
@@ -352,8 +352,8 @@ static void RackWidget_updateAdjacent(RackWidget *that) { | |||||
} | } | ||||
ModuleWidget *mw = dynamic_cast<ModuleWidget*>(w); | ModuleWidget *mw = dynamic_cast<ModuleWidget*>(w); | ||||
mw->module->rightModuleId = mwRight ? mwRight->module->id : -1; | |||||
mw->module->leftModuleId = mwLeft ? mwLeft->module->id : -1; | |||||
mw->module->leftExpander.moduleId = mwLeft ? mwLeft->module->id : -1; | |||||
mw->module->rightExpander.moduleId = mwRight ? mwRight->module->id : -1; | |||||
} | } | ||||
} | } | ||||
@@ -305,39 +305,26 @@ static void Engine_step(Engine *that) { | |||||
// Flip messages for each module | // Flip messages for each module | ||||
for (Module *module : that->internal->modules) { | for (Module *module : that->internal->modules) { | ||||
if (module->leftMessageFlipRequested) { | |||||
std::swap(module->leftProducerMessage, module->leftConsumerMessage); | |||||
module->leftMessageFlipRequested = false; | |||||
if (module->leftExpander.messageFlipRequested) { | |||||
std::swap(module->leftExpander.producerMessage, module->leftExpander.consumerMessage); | |||||
module->leftExpander.messageFlipRequested = false; | |||||
} | } | ||||
if (module->rightMessageFlipRequested) { | |||||
std::swap(module->rightProducerMessage, module->rightConsumerMessage); | |||||
module->rightMessageFlipRequested = false; | |||||
if (module->rightExpander.messageFlipRequested) { | |||||
std::swap(module->rightExpander.producerMessage, module->rightExpander.consumerMessage); | |||||
module->rightExpander.messageFlipRequested = false; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
static void Engine_updateAdjacent(Engine *that, Module *m) { | |||||
// Sync leftModule | |||||
if (m->leftModuleId >= 0) { | |||||
if (!m->leftModule || m->leftModule->id != m->leftModuleId) { | |||||
m->leftModule = that->getModule(m->leftModuleId); | |||||
static void Engine_updateExpander(Engine *that, Module::Expander *expander) { | |||||
if (expander->moduleId >= 0) { | |||||
if (!expander->module || expander->module->id != expander->moduleId) { | |||||
expander->module = that->getModule(expander->moduleId); | |||||
} | } | ||||
} | } | ||||
else { | else { | ||||
if (m->leftModule) { | |||||
m->leftModule = NULL; | |||||
} | |||||
} | |||||
// Sync rightModule | |||||
if (m->rightModuleId >= 0) { | |||||
if (!m->rightModule || m->rightModule->id != m->rightModuleId) { | |||||
m->rightModule = that->getModule(m->rightModuleId); | |||||
} | |||||
} | |||||
else { | |||||
if (m->rightModule) { | |||||
m->rightModule = NULL; | |||||
if (expander->module) { | |||||
expander->module = NULL; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -411,8 +398,10 @@ static void Engine_run(Engine *that) { | |||||
if (!internal->paused) { | if (!internal->paused) { | ||||
std::lock_guard<std::recursive_mutex> lock(internal->mutex); | std::lock_guard<std::recursive_mutex> lock(internal->mutex); | ||||
// Update expander pointers | |||||
for (Module *module : internal->modules) { | for (Module *module : internal->modules) { | ||||
Engine_updateAdjacent(that, module); | |||||
Engine_updateExpander(that, &module->leftExpander); | |||||
Engine_updateExpander(that, &module->rightExpander); | |||||
} | } | ||||
// Step modules | // Step modules | ||||
@@ -532,15 +521,15 @@ void Engine::removeModule(Module *module) { | |||||
if (paramHandle->moduleId == module->id) | if (paramHandle->moduleId == module->id) | ||||
paramHandle->module = NULL; | paramHandle->module = NULL; | ||||
} | } | ||||
// Update adjacent modules | |||||
// Update expander pointers | |||||
for (Module *m : internal->modules) { | for (Module *m : internal->modules) { | ||||
if (m->leftModule == module) { | |||||
m->leftModuleId = -1; | |||||
m->leftModule = NULL; | |||||
if (m->leftExpander.module == module) { | |||||
m->leftExpander.moduleId = -1; | |||||
m->leftExpander.module = NULL; | |||||
} | } | ||||
if (m->rightModule == module) { | |||||
m->rightModuleId = -1; | |||||
m->rightModule = NULL; | |||||
if (m->rightExpander.module == module) { | |||||
m->rightExpander.moduleId = -1; | |||||
m->rightExpander.module = NULL; | |||||
} | } | ||||
} | } | ||||
// Trigger Remove event | // Trigger Remove event | ||||
@@ -61,12 +61,12 @@ json_t *Module::toJson() { | |||||
} | } | ||||
// leftModuleId | // leftModuleId | ||||
if (leftModuleId >= 0) | |||||
json_object_set_new(rootJ, "leftModuleId", json_integer(leftModuleId)); | |||||
if (leftExpander.moduleId >= 0) | |||||
json_object_set_new(rootJ, "leftModuleId", json_integer(leftExpander.moduleId)); | |||||
// rightModuleId | // rightModuleId | ||||
if (rightModuleId >= 0) | |||||
json_object_set_new(rootJ, "rightModuleId", json_integer(rightModuleId)); | |||||
if (rightExpander.moduleId >= 0) | |||||
json_object_set_new(rootJ, "rightModuleId", json_integer(rightExpander.moduleId)); | |||||
return rootJ; | return rootJ; | ||||
} | } | ||||
@@ -115,12 +115,12 @@ void Module::fromJson(json_t *rootJ) { | |||||
// leftModuleId | // leftModuleId | ||||
json_t *leftModuleIdJ = json_object_get(rootJ, "leftModuleId"); | json_t *leftModuleIdJ = json_object_get(rootJ, "leftModuleId"); | ||||
if (leftModuleIdJ) | if (leftModuleIdJ) | ||||
leftModuleId = json_integer_value(leftModuleIdJ); | |||||
leftExpander.moduleId = json_integer_value(leftModuleIdJ); | |||||
// rightModuleId | // rightModuleId | ||||
json_t *rightModuleIdJ = json_object_get(rootJ, "rightModuleId"); | json_t *rightModuleIdJ = json_object_get(rootJ, "rightModuleId"); | ||||
if (rightModuleIdJ) | if (rightModuleIdJ) | ||||
rightModuleId = json_integer_value(rightModuleIdJ); | |||||
rightExpander.moduleId = json_integer_value(rightModuleIdJ); | |||||
} | } | ||||