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(); }