]> git.sesse.net Git - nageru/commitdiff
Support optional effects with multiple inputs.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 16 Jun 2019 17:14:24 +0000 (19:14 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 16 Jun 2019 17:14:24 +0000 (19:14 +0200)
This allows you to prune away entire sections of the chain; the typical
case is if you have an OverlayEffect(a, b) and want to disable that.
In the disabled versions of the chain, the OverlayEffect will be replaced
with an IdentityEffect that passes through a only and leaves the entire
subgraph under b noninstantiated.

nageru/scene.cpp
nageru/scene.h

index fded0e00b494c72d8740c7382de31cfa334cea25..2612de639961e8336d3eb4a6a8d8f8fb1f5f842a 100644 (file)
@@ -42,7 +42,9 @@ size_t Scene::compute_chain_number(bool is_main_chain) const
 {
        assert(chains.size() > 0);
        assert(chains.size() % 2 == 0);
-       size_t chain_number = compute_chain_number_for_block(blocks.size() - 1);
+       bitset<256> disabled = find_disabled_blocks(size_t(-1));
+
+       size_t chain_number = compute_chain_number_for_block(blocks.size() - 1, disabled);
        assert(chain_number < chains.size() / 2);
        if (is_main_chain) {
                chain_number += chains.size() / 2;
@@ -50,20 +52,74 @@ size_t Scene::compute_chain_number(bool is_main_chain) const
        return chain_number;
 }
 
-size_t Scene::compute_chain_number_for_block(size_t block_idx) const
+size_t Scene::compute_chain_number_for_block(size_t block_idx, const bitset<256> &disabled) const
 {
        Block *block = blocks[block_idx];
        size_t chain_number;
+
+       size_t currently_chosen_alternative;
+       if (disabled.test(block_idx)) {
+               // It doesn't matter, so pick 0 as the canonical choice
+               // (this is the only one that is actually instantiated).
+               currently_chosen_alternative = 0;
+       } else {
+               currently_chosen_alternative = block->currently_chosen_alternative;
+       }
+       assert(currently_chosen_alternative < block->alternatives.size());
+
        if (block_idx == 0) {
                assert(block->cardinality_base == 1);
-               chain_number = block->currently_chosen_alternative;
+               chain_number = currently_chosen_alternative;
        } else {
-               chain_number = compute_chain_number_for_block(block_idx - 1) + block->cardinality_base * block->currently_chosen_alternative;
+               chain_number = compute_chain_number_for_block(block_idx - 1, disabled) + block->cardinality_base * currently_chosen_alternative;
        }
-       assert(block->currently_chosen_alternative < int(block->alternatives.size()));
        return chain_number;
 }
 
+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);
+       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) {
+               if (disabled.test(block_idx) && blocks[block_idx]->chosen_alternative(chain_idx) != 0) {
+                       return true;
+               }
+       }
+       return false;
+}
+
 int Scene::add_input(lua_State* L)
 {
        assert(lua_gettop(L) == 1 || lua_gettop(L) == 2);
@@ -172,7 +228,6 @@ int Scene::add_optional_effect(lua_State* L)
        assert(lua_gettop(L) >= 2);
        Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
 
-       // NOTE: We only support effects with a single parent, since that's what IdentityEffect does.
        Block *block = new Block;
        block->idx = scene->blocks.size();
 
@@ -182,7 +237,7 @@ 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->inputs.push_back(scene->blocks.size() - 1);
+       find_inputs_for_block(L, scene, block);
        scene->blocks.push_back(block);
 
        return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
@@ -190,13 +245,21 @@ int Scene::add_optional_effect(lua_State* L)
 
 Effect *Scene::instantiate_effects(const Block *block, size_t chain_idx, Scene::Instantiation *instantiation)
 {
+       // Find the chosen alternative for this block in this instance.
+       EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
+
        vector<Effect *> inputs;
        for (size_t input_idx : block->inputs) {
                inputs.push_back(instantiate_effects(blocks[input_idx], chain_idx, instantiation));
-       }
 
-       // Find the chosen alternative for this block in this instance.
-       EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
+               // As a special case, we allow IdentityEffect to take only one input
+               // even if the other alternative (or alternatives) is multi-input.
+               // Thus, even if there are more than one inputs, instantiate only
+               // the first one.
+               if (chosen_type == IDENTITY_EFFECT) {
+                       break;
+               }
+       }
 
        Effect *effect;
        switch (chosen_type) {
@@ -257,7 +320,13 @@ int Scene::finalize(lua_State* L)
        }
 
        const size_t cardinality = base;
-       const size_t total_cardinality = cardinality * (only_one_mode ? 1 : 2);
+       size_t real_cardinality = 0;
+       for (size_t chain_idx = 0; chain_idx < cardinality; ++chain_idx) {
+               if (!scene->is_noncanonical_chain(chain_idx)) {
+                       ++real_cardinality;
+               }
+       }
+       const size_t total_cardinality = real_cardinality * (only_one_mode ? 1 : 2);
        if (total_cardinality > 200) {
                print_warning(L, "The given Scene will instantiate %zu different versions. This will take a lot of time and RAM to compile; see if you could limit some options by e.g. locking the input type in some cases (by giving a fixed input to add_input()).\n",
                        total_cardinality);
@@ -266,7 +335,8 @@ int Scene::finalize(lua_State* L)
        Block *output_block = scene->blocks.back();
        for (bool is_main_chain : { false, true }) {
                for (size_t chain_idx = 0; chain_idx < cardinality; ++chain_idx) {
-                       if (only_one_mode && is_main_chain != chosen_mode) {
+                       if ((only_one_mode && is_main_chain != chosen_mode) ||
+                           scene->is_noncanonical_chain(chain_idx)) {
                                scene->chains.emplace_back();
                                continue;
                        }
index bdd5d7c78c924aa561567ff55a74a42492406cc0..83346647236eb0ecda7aa512ec1fead4172940bf 100644 (file)
@@ -13,6 +13,7 @@
 // (directly to screen, RGBA) or live (Y'CbCr output).
 
 #include <stddef.h>
+#include <bitset>
 #include <functional>
 #include <map>
 #include <memory>
@@ -78,7 +79,11 @@ struct Block {
 
        // Find the chosen alternative for this block in a given instance.
        size_t chosen_alternative(size_t chain_idx) const {
-               return (chain_idx / cardinality_base) % alternatives.size();
+               if (chain_idx == size_t(-1)) {
+                       return currently_chosen_alternative;
+               } else {
+                       return (chain_idx / cardinality_base) % alternatives.size();
+               }
        }
 
        std::vector<EffectBlueprint *> alternatives;  // Must all have the same amount of inputs. Pointers to make things easier for Lua.
@@ -133,9 +138,27 @@ private:
        movit::ResourcePool *resource_pool;
 
        movit::Effect *instantiate_effects(const Block *block, size_t chain_idx, Instantiation *instantiation);
-       size_t compute_chain_number_for_block(size_t block_idx) const;
+       size_t compute_chain_number_for_block(size_t block_idx, const std::bitset<256> &disabled) const;
        static void find_inputs_for_block(lua_State *L, Scene *scene, Block *block);
 
+       // Find out which blocks (indexed by position in the “blocks” array),
+       // if any, are disabled in a given instantiation. A disabled block is
+       // one that will not be instantiated at all, because it is a secondary
+       // (ie., not the first) input of some multi-input effect that was replaced
+       // with IdentityEffect in the given instantiation.
+       //
+       // Set chain_idx to size_t(-1) to use whatever is in each block's
+       // currently_chosen_alternative.
+       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,
+       // 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
+       // it returns true.
+       bool is_noncanonical_chain(size_t chain_idx) const;
+
 public:
        Scene(Theme *theme, float aspect_nom, float aspect_denom);
        size_t compute_chain_number(bool is_main_chain) const;