Merge sbs_render_and_advance into master

Diffs=
9945a7d2d Merge sbs_render_and_advance into master (#5181)
diff --git a/.rive_head b/.rive_head
index bd6b5f4..47859ae 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-542e320d21dc6c27dca04bc7c853bd5f1ae6970a
+9945a7d2d52d1b8e40c31ec12b541de3c93e3795
diff --git a/build/premake5.lua b/build/premake5.lua
index a3a3061..9dc168a 100644
--- a/build/premake5.lua
+++ b/build/premake5.lua
@@ -108,6 +108,12 @@
         targetdir '%{cfg.system}/arm64/bin/%{cfg.buildcfg}'
         objdir '%{cfg.system}/arm64/obj/%{cfg.buildcfg}'
     end
+
+    filter "system:emscripten"
+    do
+        buildoptions {"-pthread"}
+    end
+
     filter 'configurations:debug'
     do
         defines {'DEBUG'}
diff --git a/dependencies/premake5_harfbuzz.lua b/dependencies/premake5_harfbuzz.lua
index 9140e67..99d4e2a 100644
--- a/dependencies/premake5_harfbuzz.lua
+++ b/dependencies/premake5_harfbuzz.lua
@@ -233,6 +233,11 @@
         'HB_NO_WIN1256'
     }
 
+    filter "system:emscripten"
+    do
+        buildoptions {"-pthread"}
+    end
+
     filter 'toolset:clang'
     do
         flags {'FatalWarnings'}
diff --git a/dependencies/premake5_sheenbidi.lua b/dependencies/premake5_sheenbidi.lua
index 02de111..f3652c8 100644
--- a/dependencies/premake5_sheenbidi.lua
+++ b/dependencies/premake5_sheenbidi.lua
@@ -16,6 +16,19 @@
         sheenbidi .. '/Headers'
     }
 
+    buildoptions {
+        '-Wall',
+        '-ansi',
+        '-pedantic'
+    }
+
+    linkoptions {'-r'}
+
+    filter "system:emscripten"
+    do
+        buildoptions {"-pthread"}
+    end
+
     filter 'configurations:debug'
     do
         files {
@@ -47,14 +60,6 @@
         }
     end
 
-    buildoptions {
-        '-Wall',
-        '-ansi',
-        '-pedantic'
-    }
-
-    linkoptions {'-r'}
-
     filter 'configurations:debug'
     do
         defines {'DEBUG'}
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 7fe73eb..dd1bf12 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -97,6 +97,7 @@
         kHideFG,
     };
     void draw(Renderer* renderer, DrawOption = DrawOption::kNormal);
+    void addToRenderPath(RenderPath* path, const Mat2D& transform);
 
 #ifdef TESTING
     RenderPath* clipPath() const { return m_ClipPath.get(); }
@@ -156,10 +157,47 @@
     // provided.
     int defaultStateMachineIndex() const;
 
-    /// Make an instance of this artboard, must be explictly deleted when no
-    /// longer needed.
-    // Deprecated...
-    std::unique_ptr<ArtboardInstance> instance() const;
+    /// Make an instance of this artboard.
+    template <typename T = ArtboardInstance> std::unique_ptr<T> instance() const
+    {
+        std::unique_ptr<T> artboardClone(new T);
+        artboardClone->copy(*this);
+
+        artboardClone->m_Factory = m_Factory;
+        artboardClone->m_FrameOrigin = m_FrameOrigin;
+        artboardClone->m_IsInstance = true;
+
+        std::vector<Core*>& cloneObjects = artboardClone->m_Objects;
+        cloneObjects.push_back(artboardClone.get());
+
+        if (!m_Objects.empty())
+        {
+            // Skip first object (artboard).
+            auto itr = m_Objects.begin();
+            while (++itr != m_Objects.end())
+            {
+                auto object = *itr;
+                cloneObjects.push_back(object == nullptr ? nullptr : object->clone());
+            }
+        }
+
+        for (auto animation : m_Animations)
+        {
+            artboardClone->m_Animations.push_back(animation);
+        }
+        for (auto stateMachine : m_StateMachines)
+        {
+            artboardClone->m_StateMachines.push_back(stateMachine);
+        }
+
+        if (artboardClone->initialize() != StatusCode::Ok)
+        {
+            artboardClone = nullptr;
+        }
+
+        assert(artboardClone->isInstance());
+        return artboardClone;
+    }
 
     /// Returns true if the artboard is an instance of another
     bool isInstance() const { return m_IsInstance; }
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp
index befffb8..e075c86 100644
--- a/include/rive/nested_artboard.hpp
+++ b/include/rive/nested_artboard.hpp
@@ -28,6 +28,8 @@
 
     void nest(Artboard* artboard);
 
+    ArtboardInstance* artboard() { return m_Instance.get(); }
+
     StatusCode import(ImportStack& importStack) override;
     Core* clone() const override;
     bool advance(float elapsedSeconds);
diff --git a/include/rive/shapes/path.hpp b/include/rive/shapes/path.hpp
index 0df5914..437e9ae 100644
--- a/include/rive/shapes/path.hpp
+++ b/include/rive/shapes/path.hpp
@@ -38,6 +38,7 @@
     Shape* m_Shape = nullptr;
     std::unique_ptr<CommandPath> m_CommandPath;
     std::vector<PathVertex*> m_Vertices;
+    bool m_deferredPathDirt = false;
 
 public:
     Shape* shape() const { return m_Shape; }
diff --git a/include/rive/shapes/shape.hpp b/include/rive/shapes/shape.hpp
index 9c236eb..75b9a66 100644
--- a/include/rive/shapes/shape.hpp
+++ b/include/rive/shapes/shape.hpp
@@ -26,7 +26,9 @@
     Shape();
     void buildDependencies() override;
     bool collapse(bool value) override;
+    bool canDeferPathUpdate();
     void addPath(Path* path);
+    void addToRenderPath(RenderPath* commandPath, const Mat2D& transform);
     std::vector<Path*>& paths() { return m_Paths; }
 
     bool wantDifferencePath() const { return m_WantDifferencePath; }
diff --git a/skia/renderer/build/macosx/build_skia_renderer.sh b/skia/renderer/build/macosx/build_skia_renderer.sh
index d3710a5..fe9a3b4 100755
--- a/skia/renderer/build/macosx/build_skia_renderer.sh
+++ b/skia/renderer/build/macosx/build_skia_renderer.sh
@@ -45,8 +45,7 @@
 
 export PREMAKE=$DEPENDENCIES/bin/premake5
 pushd ..
-
-$PREMAKE --file=./premake5.lua gmake2 $OTHER_OPTIONS
+$PREMAKE --scripts=../../../build --file=./premake5.lua gmake2 $OTHER_OPTIONS
 
 for var in "$@"; do
     if [[ $var = "clean" ]]; then
diff --git a/src/artboard.cpp b/src/artboard.cpp
index 74dc6c0..fa08fd1 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -16,6 +16,7 @@
 #include "rive/importers/backboard_importer.hpp"
 #include "rive/nested_artboard.hpp"
 #include "rive/animation/state_machine_instance.hpp"
+#include "rive/shapes/shape.hpp"
 
 #include <stack>
 #include <unordered_map>
@@ -452,9 +453,6 @@
                 // re-run the update.
                 if (m_DirtDepth < i)
                 {
-                    // We put this in here just to know if we need to
-                    // keep this around...
-                    assert(false);
                     break;
                 }
             }
@@ -556,7 +554,25 @@
     renderer->restore();
 }
 
-AABB Artboard::bounds() const { return AABB(0.0f, 0.0f, width(), height()); }
+void Artboard::addToRenderPath(RenderPath* path, const Mat2D& transform)
+{
+    for (auto drawable = m_FirstDrawable; drawable != nullptr; drawable = drawable->prev)
+    {
+        if (drawable->isHidden() || !drawable->is<Shape>())
+        {
+            continue;
+        }
+        Shape* shape = drawable->as<Shape>();
+        shape->addToRenderPath(path, transform);
+    }
+}
+
+AABB Artboard::bounds() const
+{
+    return m_FrameOrigin
+               ? AABB(0.0f, 0.0f, width(), height())
+               : AABB::fromLTWH(-width() * originX(), -height() * originY(), width(), height());
+}
 
 bool Artboard::isTranslucent(const LinearAnimation* anim) const
 {
@@ -656,46 +672,46 @@
     return index;
 }
 
-std::unique_ptr<ArtboardInstance> Artboard::instance() const
-{
-    std::unique_ptr<ArtboardInstance> artboardClone(new ArtboardInstance);
-    artboardClone->copy(*this);
+// std::unique_ptr<ArtboardInstance> Artboard::instance() const
+// {
+//     std::unique_ptr<ArtboardInstance> artboardClone(new ArtboardInstance);
+//     artboardClone->copy(*this);
 
-    artboardClone->m_Factory = m_Factory;
-    artboardClone->m_FrameOrigin = m_FrameOrigin;
-    artboardClone->m_IsInstance = true;
+//     artboardClone->m_Factory = m_Factory;
+//     artboardClone->m_FrameOrigin = m_FrameOrigin;
+//     artboardClone->m_IsInstance = true;
 
-    std::vector<Core*>& cloneObjects = artboardClone->m_Objects;
-    cloneObjects.push_back(artboardClone.get());
+//     std::vector<Core*>& cloneObjects = artboardClone->m_Objects;
+//     cloneObjects.push_back(artboardClone.get());
 
-    if (!m_Objects.empty())
-    {
-        // Skip first object (artboard).
-        auto itr = m_Objects.begin();
-        while (++itr != m_Objects.end())
-        {
-            auto object = *itr;
-            cloneObjects.push_back(object == nullptr ? nullptr : object->clone());
-        }
-    }
+//     if (!m_Objects.empty())
+//     {
+//         // Skip first object (artboard).
+//         auto itr = m_Objects.begin();
+//         while (++itr != m_Objects.end())
+//         {
+//             auto object = *itr;
+//             cloneObjects.push_back(object == nullptr ? nullptr : object->clone());
+//         }
+//     }
 
-    for (auto animation : m_Animations)
-    {
-        artboardClone->m_Animations.push_back(animation);
-    }
-    for (auto stateMachine : m_StateMachines)
-    {
-        artboardClone->m_StateMachines.push_back(stateMachine);
-    }
+//     for (auto animation : m_Animations)
+//     {
+//         artboardClone->m_Animations.push_back(animation);
+//     }
+//     for (auto stateMachine : m_StateMachines)
+//     {
+//         artboardClone->m_StateMachines.push_back(stateMachine);
+//     }
 
-    if (artboardClone->initialize() != StatusCode::Ok)
-    {
-        artboardClone = nullptr;
-    }
+//     if (artboardClone->initialize() != StatusCode::Ok)
+//     {
+//         artboardClone = nullptr;
+//     }
 
-    assert(artboardClone->isInstance());
-    return artboardClone;
-}
+//     assert(artboardClone->isInstance());
+//     return artboardClone;
+// }
 
 void Artboard::frameOrigin(bool value)
 {
diff --git a/src/shapes/path.cpp b/src/shapes/path.cpp
index 3732d07..5d17027 100644
--- a/src/shapes/path.cpp
+++ b/src/shapes/path.cpp
@@ -232,6 +232,10 @@
     {
         m_Shape->pathChanged();
     }
+    if (m_deferredPathDirt)
+    {
+        addDirt(ComponentDirt::Path);
+    }
 }
 
 void Path::update(ComponentDirt value)
@@ -241,6 +245,12 @@
     assert(m_CommandPath != nullptr);
     if (hasDirt(value, ComponentDirt::Path))
     {
+        if (m_Shape->canDeferPathUpdate())
+        {
+            m_deferredPathDirt = true;
+            return;
+        }
+        m_deferredPathDirt = false;
         // Build path doesn't explicitly rewind because we use it to concatenate
         // multiple built paths into a single command path (like the hit
         // tester).
diff --git a/src/shapes/path_composer.cpp b/src/shapes/path_composer.cpp
index a9c93d0..30905fc 100644
--- a/src/shapes/path_composer.cpp
+++ b/src/shapes/path_composer.cpp
@@ -32,14 +32,15 @@
 {
     if (hasDirt(value, ComponentDirt::Path))
     {
-        auto space = m_Shape->pathSpace();
-        if (m_Shape->renderOpacity() == 0 && (space & PathSpace::Clipping) != PathSpace::Clipping)
+        if (m_Shape->canDeferPathUpdate())
         {
             m_deferredPathDirt = true;
             return;
         }
         m_deferredPathDirt = false;
 
+        auto space = m_Shape->pathSpace();
+
         if ((space & PathSpace::Local) == PathSpace::Local)
         {
             if (m_LocalPath == nullptr)
diff --git a/src/shapes/points_path.cpp b/src/shapes/points_path.cpp
index 1d20b03..a6d70d6 100644
--- a/src/shapes/points_path.cpp
+++ b/src/shapes/points_path.cpp
@@ -1,6 +1,7 @@
 #include "rive/shapes/points_path.hpp"
 #include "rive/shapes/vertex.hpp"
 #include "rive/shapes/path_vertex.hpp"
+#include "rive/shapes/shape.hpp"
 #include "rive/bones/skin.hpp"
 #include "rive/span.hpp"
 
@@ -27,8 +28,10 @@
 
 void PointsPath::update(ComponentDirt value)
 {
-    if (hasDirt(value, ComponentDirt::Path) && skin() != nullptr)
+    if (hasDirt(value, ComponentDirt::Path) && skin() != nullptr && !m_Shape->canDeferPathUpdate())
     {
+        // Path tracks re-adding ComponentDirt::Path if we deferred due to to
+        // shape being invisible.
         skin()->deform(Span<Vertex*>((Vertex**)m_Vertices.data(), m_Vertices.size()));
     }
     Super::update(value);
diff --git a/src/shapes/shape.cpp b/src/shapes/shape.cpp
index 9e4e8b1..2695410 100644
--- a/src/shapes/shape.cpp
+++ b/src/shapes/shape.cpp
@@ -18,6 +18,11 @@
     m_Paths.push_back(path);
 }
 
+bool Shape::canDeferPathUpdate()
+{
+    return renderOpacity() == 0 && (pathSpace() & PathSpace::Clipping) != PathSpace::Clipping;
+}
+
 void Shape::update(ComponentDirt value)
 {
     Super::update(value);
@@ -44,6 +49,19 @@
     invalidateStrokeEffects();
 }
 
+void Shape::addToRenderPath(RenderPath* path, const Mat2D& transform)
+{
+    auto space = pathSpace();
+    if ((space & PathSpace::Local) == PathSpace::Local)
+    {
+        path->addPath(m_PathComposer.localPath(), transform * worldTransform());
+    }
+    else
+    {
+        path->addPath(m_PathComposer.worldPath(), transform);
+    }
+}
+
 void Shape::draw(Renderer* renderer)
 {
     if (renderOpacity() == 0.0f)