Move viewer hosts (skia, tess, ...) into a class

- remove a bunch of #ifdefs from viewer.cpp
- moves factory and renderer choices together
- allow for even more host variants (e.g. CoreGraphics...)

Diffs=
788d6675c Move viewer hosts (skia, tess, ...) into a class
diff --git a/.rive_head b/.rive_head
index 184f1bc..837e8db 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-a9bfb7f6d431c3a4b46ca7cb69b11a93830a8d85
+788d6675c7bb03731023b29a3a7f61deb96fb35e
diff --git a/viewer/include/viewer/viewer_host.hpp b/viewer/include/viewer/viewer_host.hpp
new file mode 100644
index 0000000..43a5ce5
--- /dev/null
+++ b/viewer/include/viewer/viewer_host.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#ifndef _RIVE_VIEWER_HOST_HPP_
+#define _RIVE_VIEWER_HOST_HPP_
+
+#include "rive/factory.hpp"
+#include "rive/renderer.hpp"
+
+#include "sokol_gfx.h"
+
+class ViewerContent;
+
+class ViewerHost {
+public:
+    virtual ~ViewerHost() {}
+
+    // subclasses can modify sg_pass_action if they wish, but need not.
+    virtual bool init(sg_pass_action*, int width, int height) = 0;
+
+    virtual void handleResize(int width, int height) = 0;
+
+    // subclasses need only override one or the other
+    virtual void beforeDefaultPass(ViewerContent*, double) {}
+    virtual void afterDefaultPass(ViewerContent*, double) {}
+
+    static std::unique_ptr<ViewerHost> Make();
+    static rive::Factory* Factory();
+};
+
+#endif
diff --git a/viewer/src/skia/skia_host.cpp b/viewer/src/skia/skia_host.cpp
new file mode 100644
index 0000000..6f0021c
--- /dev/null
+++ b/viewer/src/skia/skia_host.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#include "viewer/viewer_host.hpp"
+#include "viewer/viewer_content.hpp"
+
+#ifdef RIVE_RENDERER_SKIA
+
+#include "skia_factory.hpp"
+#include "skia_renderer.hpp"
+
+#include "include/core/SkSurface.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkSize.h"
+#include "GrDirectContext.h"
+
+sk_sp<GrDirectContext> makeSkiaContext();
+sk_sp<SkSurface> makeSkiaSurface(GrDirectContext* context, int width, int height);
+void skiaPresentSurface(sk_sp<SkSurface> surface);
+
+class SkiaViewerHost : public ViewerHost {
+public:
+    sk_sp<GrDirectContext> m_context;
+    SkISize m_dimensions;
+
+    bool init(sg_pass_action* action, int width, int height) override {
+        m_dimensions = {width, height};
+
+#if defined(SK_METAL)
+        // Skia is layered behind the Sokol view, so we need to make sure Sokol
+        // clears transparent. Skia will draw the background.
+        *action = (sg_pass_action){.colors[0] = {
+            .action = SG_ACTION_CLEAR,
+            .value =
+            { 0.0f,
+              0.0,
+              0.0f,
+              0.0 }
+        }};
+#elif defined(SK_GL)
+        // Skia commands are issued to the same GL context before Sokol, so we need
+        // to make sure Sokol does not clear the buffer.
+        *action = (sg_pass_action){.colors[0] = {.action = SG_ACTION_DONTCARE}};
+#endif
+
+        m_context = makeSkiaContext();
+        return m_context != nullptr;
+    }
+
+    void handleResize(int width, int height) override { m_dimensions = {width, height}; }
+
+    void beforeDefaultPass(ViewerContent* content, double elapsed) override {
+        m_context->resetContext();
+        auto surf = makeSkiaSurface(m_context.get(), m_dimensions.width(), m_dimensions.height());
+        SkCanvas* canvas = surf->getCanvas();
+        SkPaint paint;
+        paint.setColor(0xFF161616);
+        canvas->drawPaint(paint);
+
+        rive::SkiaRenderer skiaRenderer(canvas);
+        if (content) {
+            content->handleDraw(&skiaRenderer, elapsed);
+        }
+
+        canvas->flush();
+        skiaPresentSurface(surf);
+        sg_reset_state_cache();
+    }
+};
+
+std::unique_ptr<ViewerHost> ViewerHost::Make() { return std::make_unique<SkiaViewerHost>(); }
+
+rive::Factory* ViewerHost::Factory() {
+    static rive::SkiaFactory skiaFactory;
+    return &skiaFactory;
+}
+
+#endif
diff --git a/viewer/src/tess/tess_host.cpp b/viewer/src/tess/tess_host.cpp
new file mode 100644
index 0000000..87c22f7
--- /dev/null
+++ b/viewer/src/tess/tess_host.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#include "viewer/viewer_host.hpp"
+#include "viewer/viewer_content.hpp"
+
+#ifdef RIVE_RENDERER_TESS
+
+#include "rive/tess/sokol/sokol_tess_renderer.hpp"
+#include "viewer/tess/viewer_sokol_factory.hpp"
+
+class TessViewerHost : public ViewerHost {
+public:
+    std::unique_ptr<rive::SokolTessRenderer> m_renderer;
+
+    bool init(sg_pass_action*, int width, int height) override {
+        m_renderer = std::make_unique<rive::SokolTessRenderer>();
+        m_renderer->orthographicProjection(0.0f, width, height, 0.0f, 0.0f, 1.0f);
+        return true;
+    }
+
+    void handleResize(int width, int height) override {
+        m_renderer->orthographicProjection(0.0f, width, height, 0.0f, 0.0f, 1.0f);
+    }
+
+    void afterDefaultPass(ViewerContent* content, double elapsed) override {
+        if (content) {
+            content->handleDraw(m_renderer.get(), elapsed);
+        }
+    }
+};
+
+std::unique_ptr<ViewerHost> ViewerHost::Make() { return std::make_unique<TessViewerHost>(); }
+
+rive::Factory* ViewerHost::Factory() {
+    static ViewerSokolFactory sokolFactory;
+    return &sokolFactory;
+}
+
+#endif
diff --git a/viewer/src/viewer.cpp b/viewer/src/viewer.cpp
index a80db62..6299260 100644
--- a/viewer/src/viewer.cpp
+++ b/viewer/src/viewer.cpp
@@ -1,6 +1,7 @@
 // Viewer & Rive
 #include "viewer/viewer.hpp"
 #include "viewer/viewer_content.hpp"
+#include "viewer/viewer_host.hpp"
 #include "rive/shapes/paint/color.hpp"
 
 // Graphics and UI abstraction
@@ -14,15 +15,7 @@
 #include <stdio.h>
 #include <memory>
 
-#ifdef RIVE_RENDERER_SKIA
-#include "skia_renderer.hpp"
-sk_sp<GrDirectContext> g_SkiaContext;
-sk_sp<SkSurface> g_SkiaSurface;
-#endif
-#ifdef RIVE_RENDERER_TESS
-#include "rive/tess/sokol/sokol_tess_renderer.hpp"
-std::unique_ptr<rive::SokolTessRenderer> g_TessRenderer;
-#endif
+std::unique_ptr<ViewerHost> g_Host = ViewerHost::Make();
 std::unique_ptr<ViewerContent> g_Content = ViewerContent::TrimPath("");
 static struct { sg_pass_action pass_action; } state;
 
@@ -38,65 +31,29 @@
     };
     simgui_setup(&imguiDescriptor);
 
-#if defined(SK_METAL)
-    // Skia is layered behind the Sokol view, so we need to make sure Sokol
-    // clears transparent. Skia will draw the background.
-    state.pass_action =
-        (sg_pass_action){.colors[0] = {.action = SG_ACTION_CLEAR, .value = {0.0f, 0.0, 0.0f, 0.0}}};
-#elif defined(SK_GL)
-    // Skia commands are issued to the same GL context before Sokol, so we need
-    // to make sure Sokol does not clear the buffer.
-    state.pass_action = (sg_pass_action){.colors[0] = {.action = SG_ACTION_DONTCARE}};
-#else
-    // In every other case, Sokol is in full control, so let's clear to our bg
-    // color.
+    // If the host doesn't overwrite this (in init()), Sokol is in full control,
+    // so let's clear to our bg color.
     state.pass_action =
         (sg_pass_action){.colors[0] = {.action = SG_ACTION_CLEAR,
                                        .value = {rive::colorRed(backgroundColor) / 255.0f,
                                                  rive::colorGreen(backgroundColor) / 255.0f,
                                                  rive::colorBlue(backgroundColor) / 255.0f,
                                                  rive::colorOpacity(backgroundColor)}}};
-#endif
 
-#ifdef RIVE_RENDERER_SKIA
-    g_SkiaContext = makeSkiaContext();
-    if (!g_SkiaContext) {
-        fprintf(stderr, "failed to create skia context\n");
+    if (!g_Host->init(&state.pass_action, sapp_width(), sapp_height())) {
+        fprintf(stderr, "failed to initialize host\n");
         sapp_quit();
     }
-#endif
-#ifdef RIVE_RENDERER_TESS
-    g_TessRenderer = std::make_unique<rive::SokolTessRenderer>();
-    g_TessRenderer->orthographicProjection(0.0f, sapp_width(), sapp_height(), 0.0f, 0.0f, 1.0f);
-#endif
 }
 
 static void frame(void) {
-#ifdef RIVE_RENDERER_SKIA
-    g_SkiaContext->resetContext();
-    g_SkiaSurface = makeSkiaSurface(g_SkiaContext.get(), sapp_width(), sapp_height());
-    SkCanvas* canvas = g_SkiaSurface->getCanvas();
-    SkPaint paint;
-    paint.setColor(backgroundColor);
-    canvas->drawPaint(paint);
+    auto dur = sapp_frame_duration();
 
-    rive::SkiaRenderer skiaRenderer(canvas);
-    if (g_Content) {
-        g_Content->handleDraw(&skiaRenderer, sapp_frame_duration());
-    }
-
-    canvas->flush();
-    skiaPresentSurface(g_SkiaSurface);
-    sg_reset_state_cache();
-#endif
+    g_Host->beforeDefaultPass(g_Content.get(), dur);
 
     sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height());
 
-#ifdef RIVE_RENDERER_TESS
-    if (g_Content) {
-        g_Content->handleDraw(g_TessRenderer.get(), sapp_frame_duration());
-    }
-#endif
+    g_Host->afterDefaultPass(g_Content.get(), dur);
 
     simgui_frame_desc_t imguiDesc = {
         .width = sapp_width(),
@@ -119,10 +76,8 @@
 
 static void cleanup(void) {
     g_Content = nullptr;
-#ifdef RIVE_RENDERER_SKIA
-    g_SkiaSurface = nullptr;
-    g_SkiaContext = nullptr;
-#endif
+    g_Host = nullptr;
+
     simgui_shutdown();
     sg_shutdown();
 }
@@ -135,12 +90,7 @@
             if (g_Content) {
                 g_Content->handleResize(ev->framebuffer_width, ev->framebuffer_height);
             }
-#ifdef RIVE_RENDERER_TESS
-            if (g_TessRenderer) {
-                g_TessRenderer->orthographicProjection(
-                    0.0f, ev->framebuffer_width, ev->framebuffer_height, 0.0f, 0.0f, 1.0f);
-            }
-#endif
+            g_Host->handleResize(ev->framebuffer_width, ev->framebuffer_height);
             break;
         case SAPP_EVENTTYPE_FILES_DROPPED: {
             // Do this to make sure the graphics is bound.
diff --git a/viewer/src/viewer_content/viewer_content.cpp b/viewer/src/viewer_content/viewer_content.cpp
index c94715c..cf545ab 100644
--- a/viewer/src/viewer_content/viewer_content.cpp
+++ b/viewer/src/viewer_content/viewer_content.cpp
@@ -60,13 +60,6 @@
     printf("\n");
 }
 
-rive::Factory* ViewerContent::RiveFactory() {
-#ifdef RIVE_RENDERER_TESS
-    static ViewerSokolFactory sokolFactory;
-    return &sokolFactory;
-#endif
-#ifdef RIVE_RENDERER_SKIA
-    static rive::SkiaFactory skiaFactory;
-    return &skiaFactory;
-#endif
-}
\ No newline at end of file
+#include "viewer/viewer_host.hpp"
+
+rive::Factory* ViewerContent::RiveFactory() { return ViewerHost::Factory(); }