From 87ca88abe461522ff7386ee179a87a2ba2f5012c Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 22 Jun 2019 15:16:14 +0200 Subject: [PATCH] Add disable_if_always_disabled() to Block objects. This allows the theme to specify that a given effect only makes sense if another effect is enabled; e.g. a crop that only makes sense if immediately followed by a resize. This can cut down the number of instantiations in some cases. Also change so that 0 is no longer always the canonical choice; if disabling a block is a possibility, that is. In situations with things disabling each other transitively, this could reduce the number of instantiations further. --- nageru/scene.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++---- nageru/scene.h | 10 ++++++-- nageru/theme.cpp | 1 + 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/nageru/scene.cpp b/nageru/scene.cpp index 2612de6..70bccbe 100644 --- a/nageru/scene.cpp +++ b/nageru/scene.cpp @@ -59,9 +59,9 @@ size_t Scene::compute_chain_number_for_block(size_t block_idx, const bitset<256> size_t currently_chosen_alternative; if (disabled.test(block_idx)) { - // It doesn't matter, so pick 0 as the canonical choice + // It doesn't matter, so pick the canonical choice // (this is the only one that is actually instantiated). - currently_chosen_alternative = 0; + currently_chosen_alternative = block->canonical_alternative; } else { currently_chosen_alternative = block->currently_chosen_alternative; } @@ -80,8 +80,27 @@ bitset<256> Scene::find_disabled_blocks(size_t chain_idx) const { assert(blocks.size() < 256); - bitset<256> ret; - find_disabled_blocks(chain_idx, blocks.size() - 1, /*currently_disabled=*/false, &ret); + // 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; } @@ -113,7 +132,8 @@ bool Scene::is_noncanonical_chain(size_t chain_idx) const } assert(blocks.size() < 256); for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) { - if (disabled.test(block_idx) && blocks[block_idx]->chosen_alternative(chain_idx) != 0) { + Block *block = blocks[block_idx]; + if (disabled.test(block_idx) && block->chosen_alternative(chain_idx) != block->canonical_alternative) { return true; } } @@ -217,6 +237,15 @@ int Scene::add_effect(lua_State* L) block->alternatives.push_back(blueprint); } + int identity_index = find_index_of(block, IDENTITY_EFFECT); + if (identity_index == -1) { + block->canonical_alternative = 0; + } else { + // Pick the IdentityEffect as the canonical alternative, in case it + // helps us disable more stuff. + block->canonical_alternative = identity_index; + } + find_inputs_for_block(L, scene, block); scene->blocks.push_back(block); @@ -237,6 +266,8 @@ int Scene::add_optional_effect(lua_State* L) // An IdentityEffect will be the alternative for when the effect is disabled. block->alternatives.push_back(new EffectBlueprint(IDENTITY_EFFECT)); + block->canonical_alternative = 1; + find_inputs_for_block(L, scene, block); scene->blocks.push_back(block); @@ -633,6 +664,27 @@ int Block_disable(lua_State *L) return 0; } +int Block_always_disable_if_disabled(lua_State *L) +{ + assert(lua_gettop(L) == 2); + Block *block = *(Block **)luaL_checkudata(L, 1, "Block"); + Block *disabler_block = *(Block **)luaL_checkudata(L, 2, "Block"); + + int my_alternative = find_index_of(block, IDENTITY_EFFECT); + int their_alternative = find_index_of(disabler_block, IDENTITY_EFFECT); + if (my_alternative == -1) { + luaL_error(L, "always_disable_if_disabled() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())"); + } + if (their_alternative == -1) { + luaL_error(L, "always_disable_if_disabled() with an argument that didn't have an IdentityEffect fallback (try add_optional_effect())"); + } + + block->disablers.push_back(disabler_block->idx); + + lua_pop(L, 2); + return 0; +} + int Block_set_int(lua_State *L) { assert(lua_gettop(L) == 3); diff --git a/nageru/scene.h b/nageru/scene.h index 8334664..32b3203 100644 --- a/nageru/scene.h +++ b/nageru/scene.h @@ -78,7 +78,7 @@ struct Block { size_t cardinality_base = 0; // Find the chosen alternative for this block in a given instance. - size_t chosen_alternative(size_t chain_idx) const { + int chosen_alternative(size_t chain_idx) const { if (chain_idx == size_t(-1)) { return currently_chosen_alternative; } else { @@ -88,7 +88,12 @@ struct Block { std::vector alternatives; // Must all have the same amount of inputs. Pointers to make things easier for Lua. std::vector inputs; // One for each input of alternatives[0] (ie., typically 0 or 1, occasionally 2). + std::vector disablers; // If any of these are disabled (IdentityEffect chosen), so should this one. int currently_chosen_alternative = 0; + // What alternative to use if the block is disabled. + // Points to an alternative with IDENTITY_EFFECT if it exists + // (to disable as much as possible), otherwise 0. + int canonical_alternative = 0; bool is_input = false; // For LIVE_INPUT* only. We can't just always populate signal_to_connect, @@ -117,6 +122,7 @@ int Block_choose(lua_State* L); int Block_enable(lua_State *L); int Block_enable_if(lua_State *L); int Block_disable(lua_State *L); +int Block_always_disable_if_disabled(lua_State *L); int Block_set_int(lua_State *L); int Block_set_float(lua_State *L); int Block_set_vec3(lua_State *L); @@ -152,7 +158,7 @@ private: std::bitset<256> find_disabled_blocks(size_t chain_idx) const; void find_disabled_blocks(size_t chain_idx, size_t block_idx, bool currently_disabled, std::bitset<256> *disabled) const; - // If a block is disabled, it should always have alternative 0 chosen, + // If a block is disabled, it should always have canonical_alternative chosen, // so that we don't instantiate a bunch of irrelevant duplicates that // differ only in disabled blocks. You can check this property with // is_noncanonical_chain() and then avoid instantiating the ones where diff --git a/nageru/theme.cpp b/nageru/theme.cpp index 7c3b366..63070da 100644 --- a/nageru/theme.cpp +++ b/nageru/theme.cpp @@ -858,6 +858,7 @@ const luaL_Reg Block_funcs[] = { { "enable", Block_enable }, { "enable_if", Block_enable_if }, { "disable", Block_disable }, + { "always_disable_if_disabled", Block_always_disable_if_disabled }, { "set_int", Block_set_int }, { "set_float", Block_set_float }, { "set_vec3", Block_set_vec3 }, -- 2.39.2