add ability to attach callbacks when resolving file asset listeners

Bascially add tracking of fileAssetReferencers from file assets, so that when file assets update, they can tell things that reference them to update also.

its worth looking at the rive-flutter & runtime implementations

in cpp it looks like we can hook into the delete hooks nicely to clean up after ourselves (so that when artboards get collected we are not holding references to them from file assets)

in dart this is more of a problem, however using weakReferences we do end up seeing artboards go out of scope
but weakReferences requires us to bump to a min dart of 2.17 (we are 2.14 in flutter & 2.12 in our editor atm)

the update here also uses the referencers to mark fonts dirty when fonts are set, which causes them to be updated on the next draw cycle

(video showing font updates working properly now in dart)

https://github.com/rive-app/rive/assets/1216025/d65ea2cf-95d6-4412-b8f5-368cda609d0b

(video showing how referencers get collected and trashed in dart, it looks like we hold onto about 10 of them at a time.. but they do drop off over time. also we start with 2 references, the main artboard and the artboard instance)

https://github.com/rive-app/rive/assets/1216025/b11b7b46-aa9d-4dcc-bc11-f745d8449575

Diffs=
7bc216b03 add ability to attach callbacks when resolving file asset listeners (#6068)

Co-authored-by: Maxwell Talbot <talbot.maxwell@gmail.com>
diff --git a/.rive_head b/.rive_head
index 70cb24c..9efff06 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-8b7dd980b88815f42ee177dcda7f96dd194226fa
+7bc216b036bff5ec296f5dc14787b4779c18c871
diff --git a/dev/core_generator/pubspec.yaml b/dev/core_generator/pubspec.yaml
index 5227dc5..4fb6c60 100644
--- a/dev/core_generator/pubspec.yaml
+++ b/dev/core_generator/pubspec.yaml
@@ -1,7 +1,7 @@
 name: core_generator
 
 environment:
-  sdk: ">=2.12.0 <3.0.0"
+  sdk: ">=2.17.0 <3.0.0"
 
 dependencies:
   colorize: ^3.0.0
diff --git a/include/rive/assets/file_asset.hpp b/include/rive/assets/file_asset.hpp
index ad6b8ab..8091299 100644
--- a/include/rive/assets/file_asset.hpp
+++ b/include/rive/assets/file_asset.hpp
@@ -1,5 +1,6 @@
 #ifndef _RIVE_FILE_ASSET_HPP_
 #define _RIVE_FILE_ASSET_HPP_
+#include "rive/assets/file_asset_referencer.hpp"
 #include "rive/generated/assets/file_asset_base.hpp"
 #include "rive/span.hpp"
 #include <string>
@@ -11,6 +12,7 @@
 {
 private:
     std::vector<uint8_t> m_cdnUuid;
+    std::vector<FileAssetReferencer*> m_fileAssetReferencers;
 
 public:
     Span<const uint8_t> cdnUuid() const;
@@ -21,6 +23,26 @@
     virtual bool decode(Span<const uint8_t>, Factory*) = 0;
     virtual std::string fileExtension() const = 0;
     StatusCode import(ImportStack& importStack) override;
+    const std::vector<FileAssetReferencer*> fileAssetReferencers()
+    {
+        return m_fileAssetReferencers;
+    }
+
+    void addFileAssetReferencer(FileAssetReferencer* referencer)
+    {
+        m_fileAssetReferencers.push_back(referencer);
+    }
+
+    void removeFileAssetReferencer(FileAssetReferencer* referencer)
+    {
+        m_fileAssetReferencers.erase(
+            std::remove_if(m_fileAssetReferencers.begin(),
+                           m_fileAssetReferencers.end(),
+                           [&referencer](FileAssetReferencer* otherReferencer) {
+                               return otherReferencer == referencer;
+                           }),
+            m_fileAssetReferencers.end());
+    }
 
     std::string uniqueFilename() const;
 };
diff --git a/include/rive/assets/file_asset_referencer.hpp b/include/rive/assets/file_asset_referencer.hpp
index df300e7..1d5797a 100644
--- a/include/rive/assets/file_asset_referencer.hpp
+++ b/include/rive/assets/file_asset_referencer.hpp
@@ -9,9 +9,13 @@
 class FileAsset;
 class FileAssetReferencer
 {
+protected: 
+    FileAsset* m_fileAsset = nullptr;
 public:
-    virtual ~FileAssetReferencer() {}
-    virtual void assets(const std::vector<FileAsset*>& assets) = 0;
+
+    virtual ~FileAssetReferencer() = 0;
+    virtual void setAsset(FileAsset* asset);
+    virtual uint32_t assetId() = 0;
     StatusCode registerReferencer(ImportStack& importStack);
 };
 } // namespace rive
diff --git a/include/rive/shapes/image.hpp b/include/rive/shapes/image.hpp
index 421a404..554c25b 100644
--- a/include/rive/shapes/image.hpp
+++ b/include/rive/shapes/image.hpp
@@ -12,17 +12,17 @@
 class Image : public ImageBase, public FileAssetReferencer
 {
 private:
-    ImageAsset* m_ImageAsset = nullptr;
     Mesh* m_Mesh = nullptr;
 
 public:
     Mesh* mesh() const;
     void setMesh(Mesh* mesh);
-    ImageAsset* imageAsset() const { return m_ImageAsset; }
+    ImageAsset* imageAsset() const { return (ImageAsset*)m_fileAsset; }
     void draw(Renderer* renderer) override;
     Core* hitTest(HitInfo*, const Mat2D&) override;
     StatusCode import(ImportStack& importStack) override;
-    void assets(const std::vector<FileAsset*>& assets) override;
+    void setAsset(FileAsset*) override;
+    uint32_t assetId() override;
     Core* clone() const override;
 };
 } // namespace rive
diff --git a/include/rive/text/text_style.hpp b/include/rive/text/text_style.hpp
index 3bef063..fd82c31 100644
--- a/include/rive/text/text_style.hpp
+++ b/include/rive/text/text_style.hpp
@@ -27,9 +27,12 @@
     TextStyle();
     void buildDependencies() override;
     const rcp<Font> font() const;
-    void assets(const std::vector<FileAsset*>& assets) override;
+    void setAsset(FileAsset*) override;
+    uint32_t assetId() override;
     StatusCode import(ImportStack& importStack) override;
 
+    FontAsset* fontAsset() const { return (FontAsset*)m_fileAsset; }
+
     bool addPath(const RawPath& rawPath, float opacity);
     void rewindPath();
     void draw(Renderer* renderer);
@@ -38,7 +41,6 @@
     void updateVariableFont();
     StatusCode onAddedClean(CoreContext* context) override;
     void onDirty(ComponentDirt dirt) override;
-    // void update(ComponentDirt value) override;
 
 protected:
     void fontSizeChanged() override;
@@ -49,7 +51,6 @@
     std::unique_ptr<TextVariationHelper> m_variationHelper;
     std::unordered_map<float, std::unique_ptr<RenderPath>> m_opacityPaths;
     rcp<Font> m_variableFont;
-    FontAsset* m_fontAsset = nullptr;
     std::unique_ptr<RenderPath> m_path;
     bool m_hasContents = false;
     std::vector<Font::Coord> m_coords;
diff --git a/src/assets/file_asset_referencer.cpp b/src/assets/file_asset_referencer.cpp
index bbecf39..6c3fee2 100644
--- a/src/assets/file_asset_referencer.cpp
+++ b/src/assets/file_asset_referencer.cpp
@@ -1,9 +1,18 @@
 #include "rive/assets/file_asset_referencer.hpp"
 #include "rive/backboard.hpp"
+#include "rive/assets/file_asset.hpp"
 #include "rive/importers/backboard_importer.hpp"
 
 using namespace rive;
 
+FileAssetReferencer::~FileAssetReferencer()
+{
+    if (m_fileAsset != nullptr)
+    {
+        m_fileAsset->removeFileAssetReferencer(this);
+    }
+}
+
 StatusCode FileAssetReferencer::registerReferencer(ImportStack& importStack)
 {
     auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey);
@@ -14,4 +23,10 @@
     backboardImporter->addFileAssetReferencer(this);
 
     return StatusCode::Ok;
-}
\ No newline at end of file
+}
+
+void FileAssetReferencer::setAsset(FileAsset* asset)
+{
+    m_fileAsset = asset;
+    asset->addFileAssetReferencer(this);
+};
\ No newline at end of file
diff --git a/src/assets/font_asset.cpp b/src/assets/font_asset.cpp
index 8f41ffc..a398e55 100644
--- a/src/assets/font_asset.cpp
+++ b/src/assets/font_asset.cpp
@@ -1,3 +1,5 @@
+#include "rive/text/text_style.hpp"
+#include "rive/assets/file_asset_referencer.hpp"
 #include "rive/assets/font_asset.hpp"
 #include "rive/artboard.hpp"
 #include "rive/factory.hpp"
@@ -11,4 +13,14 @@
 }
 std::string FontAsset::fileExtension() const { return "ttf"; }
 
-void FontAsset::font(rcp<Font> font) { m_font = std::move(font); }
\ No newline at end of file
+void FontAsset::font(rcp<Font> font) { 
+    m_font = std::move(font); 
+
+    // We could try to tie this to some generic FileAssetReferencer callback 
+    // but for that we'd need this to be behind a more generic setter like 
+    // ::asset(rcp<Asset> asset)
+    for (FileAssetReferencer* fileAssetReferencer: fileAssetReferencers()){
+        static_cast<TextStyle*>(fileAssetReferencer)->addDirt(ComponentDirt::TextShape);
+    }
+    
+}
\ No newline at end of file
diff --git a/src/importers/backboard_importer.cpp b/src/importers/backboard_importer.cpp
index f0e49a9..19eb762 100644
--- a/src/importers/backboard_importer.cpp
+++ b/src/importers/backboard_importer.cpp
@@ -74,7 +74,13 @@
     }
     for (auto referencer : m_FileAssetReferencers)
     {
-        referencer->assets(m_FileAssets);
+        auto index = (size_t)referencer->assetId();
+        if (index >= m_FileAssets.size())
+        {
+            continue;
+        }
+        auto asset = m_FileAssets[index];
+        referencer->setAsset(asset);
     }
 
     return StatusCode::Ok;
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp
index 7d3c2cd..6f40b36 100644
--- a/src/shapes/image.cpp
+++ b/src/shapes/image.cpp
@@ -12,8 +12,9 @@
 void Image::draw(Renderer* renderer)
 {
     rive::RenderImage* renderImage;
-    if (m_ImageAsset == nullptr || renderOpacity() == 0.0f ||
-        (renderImage = m_ImageAsset->renderImage()) == nullptr)
+    rive::ImageAsset* asset = this->imageAsset();
+    if (asset == nullptr || renderOpacity() == 0.0f ||
+        (renderImage = asset->renderImage()) == nullptr)
     {
         return;
     }
@@ -46,7 +47,7 @@
 {
     // TODO: handle clip?
 
-    auto renderImage = m_ImageAsset->renderImage();
+    auto renderImage = imageAsset()->renderImage();
     int width = renderImage->width();
     int height = renderImage->height();
 
@@ -79,22 +80,21 @@
     return Super::import(importStack);
 }
 
-void Image::assets(const std::vector<FileAsset*>& assets)
+// Question: thoughts on this? it looks a bit odd to me,
+// maybe there's a trick i'm missing here .. (could also implement getAssetId...)
+uint32_t Image::assetId() { return ImageBase::assetId(); }
+
+void Image::setAsset(FileAsset* asset)
 {
-    if ((size_t)assetId() >= assets.size())
-    {
-        return;
-    }
-    auto asset = assets[assetId()];
     if (asset->is<ImageAsset>())
     {
-        m_ImageAsset = asset->as<ImageAsset>();
+        FileAssetReferencer::setAsset(asset);
 
         // If we have a mesh and we're in the source artboard, let's initialize
         // the mesh buffers.
         if (m_Mesh != nullptr && !artboard()->isInstance())
         {
-            m_Mesh->initializeSharedBuffers(m_ImageAsset->renderImage());
+            m_Mesh->initializeSharedBuffers(imageAsset()->renderImage());
         }
     }
 }
@@ -102,7 +102,10 @@
 Core* Image::clone() const
 {
     Image* twin = ImageBase::clone()->as<Image>();
-    twin->m_ImageAsset = m_ImageAsset;
+    if (m_fileAsset != nullptr)
+    {
+        twin->setAsset(m_fileAsset);
+    }
     return twin;
 }
 
diff --git a/src/text/text_style.cpp b/src/text/text_style.cpp
index 21e5b65..a43a1c8 100644
--- a/src/text/text_style.cpp
+++ b/src/text/text_style.cpp
@@ -80,12 +80,15 @@
     {
         return m_variableFont;
     }
-    return m_fontAsset == nullptr ? nullptr : m_fontAsset->font();
+
+    auto asset = fontAsset();
+    return asset == nullptr ? nullptr : asset->font();
 }
 
 void TextStyle::updateVariableFont()
 {
-    rcp<Font> baseFont = m_fontAsset == nullptr ? nullptr : m_fontAsset->font();
+    auto asset = fontAsset();
+    rcp<Font> baseFont = asset == nullptr ? nullptr : asset->font();
     if (baseFont == nullptr)
     {
         // Not ready yet.
@@ -185,16 +188,13 @@
     }
 }
 
-void TextStyle::assets(const std::vector<FileAsset*>& assets)
+uint32_t TextStyle::assetId() { return this->fontAssetId(); }
+
+void TextStyle::setAsset(FileAsset* asset)
 {
-    if ((size_t)fontAssetId() >= assets.size())
-    {
-        return;
-    }
-    auto asset = assets[fontAssetId()];
     if (asset->is<FontAsset>())
     {
-        m_fontAsset = asset->as<FontAsset>();
+        FileAssetReferencer::setAsset(asset);
     }
 }
 
@@ -217,6 +217,10 @@
 Core* TextStyle::clone() const
 {
     TextStyle* twin = TextStyleBase::clone()->as<TextStyle>();
-    twin->m_fontAsset = m_fontAsset;
+    if (m_fileAsset != nullptr)
+    {
+        twin->setAsset(m_fileAsset);
+    }
+
     return twin;
 }
\ No newline at end of file
diff --git a/viewer/build/premake5_viewer.lua b/viewer/build/premake5_viewer.lua
index 0d100c0..6c06181 100644
--- a/viewer/build/premake5_viewer.lua
+++ b/viewer/build/premake5_viewer.lua
@@ -19,6 +19,9 @@
     dofile(path.join(path.getabsolute(rive) .. '/build', 'premake5.lua'))
 end
 
+
+dofile(path.join(path.getabsolute(rive) .. '/cg_renderer/build', 'premake5.lua'))
+
 project 'rive_viewer'
 do
     if _OPTIONS.renderer == 'tess' then
@@ -75,7 +78,8 @@
             'Cocoa.framework',
             'IOKit.framework',
             'CoreVideo.framework',
-            'OpenGL.framework'
+            'OpenGL.framework',
+            'rive_cg_renderer'
         }
         files {
             '../src/**.m',
@@ -221,7 +225,7 @@
         }
         links {
             'skia',
-            'rive_skia_renderer'
+            'rive_skia_renderer',
         }
     end
 
diff --git a/viewer/include/viewer/viewer_content.hpp b/viewer/include/viewer/viewer_content.hpp
index 2dde428..f19f138 100644
--- a/viewer/include/viewer/viewer_content.hpp
+++ b/viewer/include/viewer/viewer_content.hpp
@@ -65,7 +65,6 @@
     static std::unique_ptr<ViewerContent> TrimPath(const char[]);
 
     static std::vector<uint8_t> LoadFile(const char path[]);
-    static void DumpCounters(const char label[]);
 
     // Abstracts which rive Factory is currently used.
     static rive::Factory* RiveFactory();
diff --git a/viewer/src/viewer_content/scene_content.cpp b/viewer/src/viewer_content/scene_content.cpp
index 5b94ea3..6e66604 100644
--- a/viewer/src/viewer_content/scene_content.cpp
+++ b/viewer/src/viewer_content/scene_content.cpp
@@ -122,8 +122,6 @@
         {
             m_CurrentScene->inputCount();
         }
-
-        DumpCounters("After loading file");
     }
 
     void initAnimation(int index)
@@ -140,8 +138,6 @@
             m_CurrentScene = m_ArtboardInstance->animationAt(index);
             m_CurrentScene->inputCount();
         }
-
-        DumpCounters("After loading file");
     }
 
 public:
diff --git a/viewer/src/viewer_content/viewer_content.cpp b/viewer/src/viewer_content/viewer_content.cpp
index 354f6f8..e41da51 100644
--- a/viewer/src/viewer_content/viewer_content.cpp
+++ b/viewer/src/viewer_content/viewer_content.cpp
@@ -5,7 +5,7 @@
 #include "viewer/viewer_content.hpp"
 #include <vector>
 
-ViewerContent::~ViewerContent() { DumpCounters("After deleting content"); }
+ViewerContent::~ViewerContent() {}
 
 const char* gCounterNames[] = {
     "file",
@@ -46,22 +46,6 @@
     return bytes;
 }
 
-void ViewerContent::DumpCounters(const char label[])
-{
-    assert(sizeof(gCounterNames) / sizeof(gCounterNames[0]) == rive::Counter::kLastType + 1);
-
-    if (label == nullptr)
-    {
-        label = "Counters";
-    }
-    printf("%s:", label);
-    for (int i = 0; i <= rive::Counter::kLastType; ++i)
-    {
-        printf(" [%s]:%d", gCounterNames[i], rive::Counter::counts[i]);
-    }
-    printf("\n");
-}
-
 #ifndef RIVE_SKIP_IMGUI
 #include "viewer/viewer_host.hpp"