add advanced phase to render loop (#10318) af18705be2
* add reset phase to render loop

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index 8949345..b1ecd35 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-559f237078e0e1430a661766c071ce22bd11abc7
+af18705be2294cf120d220bd89a6d64adcd17fd1
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 71443a2..f53f129 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -125,6 +125,7 @@
 
     bool advanceAndApply(float secs) override;
     void advancedDataContext();
+    void reset();
     std::string name() const override;
     HitResult pointerMove(Vec2D position, float timeStamp = 0) override;
     HitResult pointerDown(Vec2D position) override;
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 052ab82..c879f42 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -2,6 +2,7 @@
 #define _RIVE_ARTBOARD_HPP_
 
 #include "rive/advance_flags.hpp"
+#include "rive/resetting_component.hpp"
 #include "rive/animation/linear_animation.hpp"
 #include "rive/animation/state_machine.hpp"
 #include "rive/core_context.hpp"
@@ -55,7 +56,10 @@
 typedef float (*RootTransformCallback)(void*, float, float, bool);
 #endif
 
-class Artboard : public ArtboardBase, public CoreContext, public Virtualizable
+class Artboard : public ArtboardBase,
+                 public CoreContext,
+                 public Virtualizable,
+                 public ResettingComponent
 {
     friend class File;
     friend class ArtboardImporter;
@@ -198,6 +202,7 @@
                          AdvanceFlags flags = AdvanceFlags::AdvanceNested |
                                               AdvanceFlags::Animate |
                                               AdvanceFlags::NewFrame);
+    void reset() override;
     uint8_t drawOrderChangeCounter() { return m_drawOrderChangeCounter; }
     Drawable* firstDrawable() { return m_FirstDrawable; };
 
diff --git a/include/rive/artboard_component_list.hpp b/include/rive/artboard_component_list.hpp
index b9fac24..5820a91 100644
--- a/include/rive/artboard_component_list.hpp
+++ b/include/rive/artboard_component_list.hpp
@@ -3,6 +3,7 @@
 #include "rive/generated/artboard_component_list_base.hpp"
 #include "rive/layout/artboard_component_list_override.hpp"
 #include "rive/advancing_component.hpp"
+#include "rive/resetting_component.hpp"
 #include "rive/animation/state_machine_instance.hpp"
 #include "rive/artboard.hpp"
 #include "rive/property_recorder.hpp"
@@ -21,6 +22,7 @@
 class ArtboardComponentList : public ArtboardComponentListBase,
                               public ArtboardHost,
                               public AdvancingComponent,
+                              public ResettingComponent,
                               public LayoutNodeProvider,
                               public DataBindListItemConsumer,
                               public VirtualizingComponent
@@ -43,6 +45,7 @@
     bool advanceComponent(float elapsedSeconds,
                           AdvanceFlags flags = AdvanceFlags::Animate |
                                                AdvanceFlags::NewFrame) override;
+    void reset() override;
     AABB layoutBounds() override;
     AABB layoutBoundsForNode(int index) override;
     void markHostingLayoutDirty(ArtboardInstance* artboardInstance) override;
@@ -74,7 +77,7 @@
         bool shouldForceUpdateLayoutBounds = false) override;
     bool isLayoutProvider() override { return true; }
     size_t numLayoutNodes() override { return m_listItems.size(); }
-    void reset();
+    void clear();
     void file(File*) override;
     File* file() const override;
     Core* clone() const override;
@@ -89,6 +92,7 @@
         m_visibleStartIndex = start;
         m_visibleEndIndex = end;
     }
+    void shouldResetInstances(bool value) { m_shouldResetInstances = value; }
     void setVirtualizablePosition(int index, Vec2D position) override;
     void createArtboardAt(int index);
     void addArtboardAt(std::unique_ptr<ArtboardInstance> artboard, int index);
@@ -149,6 +153,7 @@
     void attachArtboardOverride(ArtboardInstance*,
                                 rcp<ViewModelInstanceListItem>);
     void clearArtboardOverride(ArtboardInstance*);
+    bool m_shouldResetInstances = false;
 };
 } // namespace rive
 
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp
index e9fb6cf..5d7902d 100644
--- a/include/rive/nested_artboard.hpp
+++ b/include/rive/nested_artboard.hpp
@@ -8,6 +8,7 @@
 #include "rive/hit_info.hpp"
 #include "rive/span.hpp"
 #include "rive/advancing_component.hpp"
+#include "rive/resetting_component.hpp"
 #include "rive/viewmodel/viewmodel_instance_artboard.hpp"
 #include <stdio.h>
 
@@ -22,6 +23,7 @@
 class File;
 class NestedArtboard : public NestedArtboardBase,
                        public AdvancingComponent,
+                       public ResettingComponent,
                        public ArtboardHost
 {
 protected:
@@ -101,6 +103,7 @@
     bool advanceComponent(float elapsedSeconds,
                           AdvanceFlags flags = AdvanceFlags::Animate |
                                                AdvanceFlags::NewFrame) override;
+    void reset() override;
     Artboard* parentArtboard() override { return artboard(); }
     Vec2D hostTransformPoint(const Vec2D&, ArtboardInstance*) override;
     bool hitTestHost(const Vec2D& position,
diff --git a/include/rive/resetting_component.hpp b/include/rive/resetting_component.hpp
new file mode 100644
index 0000000..5e4c30b
--- /dev/null
+++ b/include/rive/resetting_component.hpp
@@ -0,0 +1,15 @@
+#ifndef _RIVE_RESETTING_COMPONENT_HPP_
+#define _RIVE_RESETTING_COMPONENT_HPP_
+
+namespace rive
+{
+class Component;
+class ResettingComponent
+{
+public:
+    virtual void reset() = 0;
+    static ResettingComponent* from(Component* component);
+};
+} // namespace rive
+
+#endif
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index 1b59c20..563430b 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -1797,6 +1797,12 @@
     }
 }
 
+void StateMachineInstance::reset()
+{
+    advancedDataContext();
+    m_artboardInstance->reset();
+}
+
 bool StateMachineInstance::advanceAndApply(float seconds)
 {
     bool keepGoing = this->advance(seconds, true);
@@ -1829,7 +1835,7 @@
         {
             keepGoing = true;
         }
-        advancedDataContext();
+        reset();
 
         if (!m_artboardInstance->hasDirt(ComponentDirt::Components))
         {
diff --git a/src/artboard.cpp b/src/artboard.cpp
index ef427c8..659e8d5 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -993,6 +993,18 @@
     return didUpdate;
 }
 
+void Artboard::reset()
+{
+    for (auto dep : m_DependencyOrder)
+    {
+        auto adv = ResettingComponent::from(dep);
+        if (adv != nullptr)
+        {
+            adv->reset();
+        }
+    }
+}
+
 bool Artboard::advance(float elapsedSeconds, AdvanceFlags flags)
 {
     AdvanceFlags advancingFlags = flags;
diff --git a/src/artboard_component_list.cpp b/src/artboard_component_list.cpp
index dd5eea7..746ad7f 100644
--- a/src/artboard_component_list.cpp
+++ b/src/artboard_component_list.cpp
@@ -10,9 +10,9 @@
 using namespace rive;
 
 ArtboardComponentList::ArtboardComponentList() {}
-ArtboardComponentList::~ArtboardComponentList() { reset(); }
+ArtboardComponentList::~ArtboardComponentList() { clear(); }
 
-void ArtboardComponentList::reset()
+void ArtboardComponentList::clear()
 {
     for (auto& artboard : m_artboardInstancesMap)
     {
@@ -334,6 +334,26 @@
     return keepGoing;
 }
 
+void ArtboardComponentList::reset()
+{
+    for (auto& item : m_listItems)
+    {
+        if (m_shouldResetInstances)
+        {
+            auto viewModelInstance = item->viewModelInstance();
+            if (viewModelInstance != nullptr)
+            {
+                viewModelInstance->advanced();
+            }
+        }
+        auto itr = m_artboardInstancesMap.find(item);
+        if (itr != m_artboardInstancesMap.end())
+        {
+            m_artboardInstancesMap[item]->reset();
+        }
+    }
+}
+
 AABB ArtboardComponentList::layoutBounds()
 {
     return AABB(0, 0, m_layoutSize.x, m_layoutSize.y);
@@ -577,7 +597,7 @@
 }
 
 void ArtboardComponentList::clearDataContext() {}
-void ArtboardComponentList::unbind() { reset(); }
+void ArtboardComponentList::unbind() { clear(); }
 void ArtboardComponentList::updateDataBinds()
 {
     for (int i = 0; i < artboardCount(); i++)
diff --git a/src/data_bind/data_bind.cpp b/src/data_bind/data_bind.cpp
index 687f2c7..fb274ca 100644
--- a/src/data_bind/data_bind.cpp
+++ b/src/data_bind/data_bind.cpp
@@ -28,6 +28,7 @@
 #include "rive/data_bind/converters/formula/formula_token.hpp"
 #include "rive/animation/transition_viewmodel_condition.hpp"
 #include "rive/animation/state_machine.hpp"
+#include "rive/artboard_component_list.hpp"
 #include "rive/importers/artboard_importer.hpp"
 #include "rive/importers/state_machine_importer.hpp"
 #include "rive/importers/backboard_importer.hpp"
@@ -152,6 +153,16 @@
         value->ref();
     }
     m_Source = value;
+
+    // We treat this as a special case. If an ArtboardComponentList's list
+    // property is bound to a number instance, we know that the component
+    // will be provided with dettached view model instances that need to be
+    // advanced explicitly
+    if (m_Source && target() && target()->is<ArtboardComponentList>())
+    {
+        target()->as<ArtboardComponentList>()->shouldResetInstances(
+            m_Source->coreType() == ViewModelInstanceNumberBase::typeKey);
+    }
 }
 
 void DataBind::clearSource()
diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp
index b26f872..1dcd524 100644
--- a/src/nested_artboard.cpp
+++ b/src/nested_artboard.cpp
@@ -517,6 +517,14 @@
     return keepGoing;
 }
 
+void NestedArtboard::reset()
+{
+    if (m_Artboard)
+    {
+        m_Artboard->reset();
+    }
+}
+
 void NestedArtboard::file(File* value) { m_file = value; }
 
 File* NestedArtboard::file() const { return m_file; }
diff --git a/src/resetting_component.cpp b/src/resetting_component.cpp
new file mode 100644
index 0000000..074c99a
--- /dev/null
+++ b/src/resetting_component.cpp
@@ -0,0 +1,23 @@
+#include "rive/component.hpp"
+#include "rive/resetting_component.hpp"
+#include "rive/artboard.hpp"
+#include "rive/artboard_component_list.hpp"
+#include "rive/nested_artboard.hpp"
+#include "rive/nested_artboard_layout.hpp"
+#include "rive/nested_artboard_leaf.hpp"
+
+using namespace rive;
+
+ResettingComponent* ResettingComponent::from(Component* component)
+{
+    switch (component->coreType())
+    {
+        case NestedArtboardLeaf::typeKey:
+        case NestedArtboardLayout::typeKey:
+        case NestedArtboard::typeKey:
+            return component->as<NestedArtboard>();
+        case ArtboardComponentListBase::typeKey:
+            return component->as<ArtboardComponentList>();
+    }
+    return nullptr;
+}
\ No newline at end of file
diff --git a/tests/unit_tests/assets/reset_phase.riv b/tests/unit_tests/assets/reset_phase.riv
new file mode 100644
index 0000000..d1e7e67
--- /dev/null
+++ b/tests/unit_tests/assets/reset_phase.riv
Binary files differ
diff --git a/tests/unit_tests/runtime/component_list_test.cpp b/tests/unit_tests/runtime/component_list_test.cpp
index fa4ff65..d2ff7cb 100644
--- a/tests/unit_tests/runtime/component_list_test.cpp
+++ b/tests/unit_tests/runtime/component_list_test.cpp
@@ -600,4 +600,38 @@
     }
 
     CHECK(silver.matches("artboard_list_overrides_vertical"));
+}
+
+TEST_CASE("Number to Lists reset triggers correctly", "[silver]")
+{
+    rive::SerializingFactory silver;
+    auto file = ReadRiveFile("assets/reset_phase.riv", &silver);
+
+    auto artboard = file->artboardNamed("multi-main");
+
+    silver.frameSize(artboard->width(), artboard->height());
+
+    REQUIRE(artboard != nullptr);
+    auto stateMachine = artboard->stateMachineAt(0);
+    int viewModelId = artboard.get()->viewModelId();
+
+    auto vmi = viewModelId == -1
+                   ? file->createViewModelInstance(artboard.get())
+                   : file->createViewModelInstance(viewModelId, 0);
+
+    stateMachine->bindViewModelInstance(vmi);
+    stateMachine->advanceAndApply(0.1f);
+
+    auto renderer = silver.makeRenderer();
+    artboard->draw(renderer.get());
+
+    int frames = (int)(3.0f / 0.16f);
+    for (int i = 0; i < frames; i++)
+    {
+        silver.addFrame();
+        stateMachine->advanceAndApply(0.16f);
+        artboard->draw(renderer.get());
+    }
+
+    CHECK(silver.matches("reset_phase_multi_main"));
 }
\ No newline at end of file
diff --git a/tests/unit_tests/silvers/reset_phase_multi_main.sriv b/tests/unit_tests/silvers/reset_phase_multi_main.sriv
new file mode 100644
index 0000000..f39dc82
--- /dev/null
+++ b/tests/unit_tests/silvers/reset_phase_multi_main.sriv
Binary files differ