@@ -37,36 +37,39 @@ struct Module { | |||
std::vector<Light> lights; | |||
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. | |||
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); | |||
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 | |||
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 { | |||
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) { | |||
std::lock_guard<std::recursive_mutex> lock(internal->mutex); | |||
// Update expander pointers | |||
for (Module *module : internal->modules) { | |||
Engine_updateAdjacent(that, module); | |||
Engine_updateExpander(that, &module->leftExpander); | |||
Engine_updateExpander(that, &module->rightExpander); | |||
} | |||
// Step modules | |||
@@ -532,15 +521,15 @@ void Engine::removeModule(Module *module) { | |||
if (paramHandle->moduleId == module->id) | |||
paramHandle->module = NULL; | |||
} | |||
// Update adjacent modules | |||
// Update expander pointers | |||
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 | |||
@@ -61,12 +61,12 @@ json_t *Module::toJson() { | |||
} | |||
// 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 | |||
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; | |||
} | |||
@@ -115,12 +115,12 @@ void Module::fromJson(json_t *rootJ) { | |||
// leftModuleId | |||
json_t *leftModuleIdJ = json_object_get(rootJ, "leftModuleId"); | |||
if (leftModuleIdJ) | |||
leftModuleId = json_integer_value(leftModuleIdJ); | |||
leftExpander.moduleId = json_integer_value(leftModuleIdJ); | |||
// rightModuleId | |||
json_t *rightModuleIdJ = json_object_get(rootJ, "rightModuleId"); | |||
if (rightModuleIdJ) | |||
rightModuleId = json_integer_value(rightModuleIdJ); | |||
rightExpander.moduleId = json_integer_value(rightModuleIdJ); | |||
} | |||