Vulkan!

Initial Vulkan implementation for PLS.

Shaders use @PLS_IMPL_NONE, which is an incomplete implementation of pixel local storage, but it's enough to get started.

Diffs=
ca56d7565 Vulkan! (#7553)

Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
Co-authored-by: DragoČ™ Tiselice <dragostiselice@gmail.com>
diff --git a/.rive_head b/.rive_head
index 2db75dc..f2c8502 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-63b6a13d3a2adc1c555747fcd76234360bad7a1d
+ca56d756514d50c68ea3306739e6ad177caee2ab
diff --git a/include/rive/refcnt.hpp b/include/rive/refcnt.hpp
index ed2b31b..2b0b5e4 100644
--- a/include/rive/refcnt.hpp
+++ b/include/rive/refcnt.hpp
@@ -35,25 +35,23 @@
 public:
     RefCnt() : m_refcnt(1) {}
 
-    ~RefCnt() { assert(this->debugging_refcnt() == 1); }
-
     void ref() const { (void)m_refcnt.fetch_add(+1, std::memory_order_relaxed); }
 
     void unref() const
     {
         if (1 == m_refcnt.fetch_add(-1, std::memory_order_acq_rel))
         {
-#ifndef NDEBUG
-            // we restore the "1" in debug builds just to make our destructor happy
-            (void)m_refcnt.fetch_add(+1, std::memory_order_relaxed);
-#endif
-            delete static_cast<const T*>(this);
+            static_cast<const T*>(this)->onRefCntReachedZero();
         }
     }
 
     // not reliable in actual threaded scenarios, but useful (perhaps) for debugging
     int32_t debugging_refcnt() const { return m_refcnt.load(std::memory_order_relaxed); }
 
+protected:
+    // Can be overloaded in the subclass if specialized delete behavior is required.
+    void onRefCntReachedZero() const { delete static_cast<const T*>(this); }
+
 private:
     // mutable, so can be changed even on a const object
     mutable std::atomic<int32_t> m_refcnt;