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