+bitset<256> Scene::find_disabled_blocks(size_t chain_idx) const
+{
+ assert(blocks.size() < 256);
+
+ // The find_disabled_blocks() recursion logic needs only one pass by itself,
+ // but the disabler logic is not so smart, so we just run multiple times
+ // until it converges.
+ bitset<256> prev, ret;
+ do {
+ find_disabled_blocks(chain_idx, blocks.size() - 1, /*currently_disabled=*/false, &ret);
+ prev = ret;
+ for (Block *block : blocks) {
+ EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
+ if (ret.test(block->idx) || chosen_type == IDENTITY_EFFECT) continue; // Already disabled.
+
+ for (Block::Index disabler_idx : block->disablers) {
+ Block *disabler = blocks[disabler_idx];
+ EffectType chosen_type = disabler->alternatives[disabler->chosen_alternative(chain_idx)]->effect_type;
+ if (ret.test(disabler->idx) || chosen_type == IDENTITY_EFFECT) {
+ ret.set(block->idx);
+ break;
+ }
+ }
+ }
+ } while (prev != ret);
+ return ret;
+}
+
+void Scene::find_disabled_blocks(size_t chain_idx, size_t block_idx, bool currently_disabled, bitset<256> *disabled) const
+{
+ if (currently_disabled) {
+ disabled->set(block_idx);
+ }
+ Block *block = blocks[block_idx];
+ EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
+ for (size_t input_idx = 0; input_idx < block->inputs.size(); ++input_idx) {
+ if (chosen_type == IDENTITY_EFFECT && input_idx > 0) {
+ // Multi-input effect that has been replaced by
+ // IdentityEffect, so every effect but the first are
+ // disabled and will not participate in the chain.
+ find_disabled_blocks(chain_idx, block->inputs[input_idx], /*currently_disabled=*/true, disabled);
+ } else {
+ // Just keep on recursing down.
+ find_disabled_blocks(chain_idx, block->inputs[input_idx], currently_disabled, disabled);
+ }
+ }
+}
+
+bool Scene::is_noncanonical_chain(size_t chain_idx) const
+{
+ bitset<256> disabled = find_disabled_blocks(chain_idx);
+ if (disabled.none()) {
+ return false;
+ }
+ assert(blocks.size() < 256);
+ for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
+ Block *block = blocks[block_idx];
+ if (disabled.test(block_idx) && block->chosen_alternative(chain_idx) != block->canonical_alternative) {
+ return true;
+ }
+ }
+ return false;
+}
+