mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
* Add profiling macros and optick profiler * Update premake5_optick.lua * Added more profiling markers * Remove microprofiler option for now as not implemented * Added more markers ro rendering code * Update Optick build premake only for windows builds * More build file changes for optick with windows * Change how optick is built on windows * Update premake5_v2.lua * Update build files to use with_optick option * Added ScopeName macro to appease ASAN * Push to rekick jobs * Delete ProfilerMacros.h * Create profiler_macros.h * Update path_fiddle.cpp * Update fiddle_context.hpp * Update for rename of header * More header changes * Update fiddle_context.hpp * Update profiler_macros.h * Update for clang format * Update fiddle_context.hpp * Update profiler_macros.h * Update profiler_macros.h * Changed premake values on Chris' comments * Removed multiple define fro RIVE_OPTICK and now in rive_build_config.lua * Added Optick URL and Version to rive_build_config Co-authored-by: John White <aliasbinman@gmail.com>
977 lines
28 KiB
C++
977 lines
28 KiB
C++
#include "fiddle_context.hpp"
|
|
|
|
#include "rive/math/simd.hpp"
|
|
#include "rive/artboard.hpp"
|
|
#include "rive/file.hpp"
|
|
#include "rive/layout.hpp"
|
|
#include "rive/animation/state_machine_instance.hpp"
|
|
#include "rive/static_scene.hpp"
|
|
#include "rive/profiler/profiler_macros.h"
|
|
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <vector>
|
|
#include <sstream>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define GLFW_INCLUDE_NONE
|
|
#include "GLFW/glfw3.h"
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten/emscripten.h>
|
|
#include <emscripten/html5.h>
|
|
#include <sstream>
|
|
#endif
|
|
|
|
using namespace rive;
|
|
|
|
constexpr static char kMoltenVKICD[] =
|
|
"dependencies/MoltenVK/Package/Release/MoltenVK/dynamic/dylib/macOS/"
|
|
"MoltenVK_icd.json";
|
|
|
|
constexpr static char kSwiftShaderICD[] = "dependencies/SwiftShader/build/"
|
|
#ifdef __APPLE__
|
|
"Darwin"
|
|
#elif defined(_WIN32)
|
|
"Windows"
|
|
#else
|
|
"Linux"
|
|
#endif
|
|
"/vk_swiftshader_icd.json";
|
|
static FiddleContextOptions options;
|
|
static GLFWwindow* window = nullptr;
|
|
static int msaa = 0;
|
|
static bool forceAtomicMode = false;
|
|
static bool wireframe = false;
|
|
static bool disableFill = false;
|
|
static bool disableStroke = false;
|
|
static bool clockwiseFill = false;
|
|
static bool hotloadShaders = false;
|
|
|
|
static std::unique_ptr<FiddleContext> fiddleContext;
|
|
|
|
static float2 pts[] = {{260 + 2 * 100, 60 + 2 * 500},
|
|
{260 + 2 * 257, 60 + 2 * 233},
|
|
{260 + 2 * -100, 60 + 2 * 300},
|
|
{260 + 2 * 100, 60 + 2 * 200},
|
|
{260 + 2 * 250, 60 + 2 * 0},
|
|
{260 + 2 * 400, 60 + 2 * 200},
|
|
{260 + 2 * 213, 60 + 2 * 200},
|
|
{260 + 2 * 213, 60 + 2 * 300},
|
|
{260 + 2 * 391, 60 + 2 * 480},
|
|
{1400, 1400}}; // Feather control.
|
|
constexpr static int NUM_INTERACTIVE_PTS = sizeof(pts) / sizeof(*pts);
|
|
|
|
static float strokeWidth = 70;
|
|
|
|
static float2 translate;
|
|
static float scale = 1;
|
|
|
|
static StrokeJoin join = StrokeJoin::round;
|
|
static StrokeCap cap = StrokeCap::round;
|
|
|
|
static bool doClose = false;
|
|
static bool paused = false;
|
|
|
|
static int dragIdx = -1;
|
|
static float2 dragLastPos;
|
|
|
|
static int animation = -1;
|
|
static int stateMachine = -1;
|
|
static int horzRepeat = 0;
|
|
static int upRepeat = 0;
|
|
static int downRepeat = 0;
|
|
|
|
std::unique_ptr<File> rivFile;
|
|
std::vector<std::unique_ptr<Artboard>> artboards;
|
|
std::vector<std::unique_ptr<Scene>> scenes;
|
|
std::vector<rive::rcp<rive::ViewModelInstance>> viewModelInstances;
|
|
|
|
static void clear_scenes()
|
|
{
|
|
artboards.clear();
|
|
scenes.clear();
|
|
viewModelInstances.clear();
|
|
}
|
|
|
|
static void make_scenes(size_t count)
|
|
{
|
|
clear_scenes();
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
// Tip: you can change the artboard shown here
|
|
// auto artboard = rivFile->artboardAt(2);
|
|
auto artboard = rivFile->artboardDefault();
|
|
std::unique_ptr<Scene> scene;
|
|
if (stateMachine >= 0)
|
|
{
|
|
scene = artboard->stateMachineAt(stateMachine);
|
|
}
|
|
else if (animation >= 0)
|
|
{
|
|
scene = artboard->animationAt(animation);
|
|
}
|
|
else
|
|
{
|
|
scene = artboard->animationAt(0);
|
|
}
|
|
if (scene == nullptr)
|
|
{
|
|
// This is a riv without any animations or state machines. Just draw
|
|
// the artboard.
|
|
scene = std::make_unique<StaticScene>(artboard.get());
|
|
}
|
|
|
|
int viewModelId = artboard.get()->viewModelId();
|
|
viewModelInstances.push_back(
|
|
viewModelId == -1
|
|
? rivFile->createViewModelInstance(artboard.get())
|
|
: rivFile->createViewModelInstance(viewModelId, 0));
|
|
artboard->bindViewModelInstance(viewModelInstances.back());
|
|
if (viewModelInstances.back() != nullptr)
|
|
{
|
|
scene->bindViewModelInstance(viewModelInstances.back());
|
|
}
|
|
|
|
scene->advanceAndApply(scene->durationSeconds() * i / count);
|
|
artboards.push_back(std::move(artboard));
|
|
scenes.push_back(std::move(scene));
|
|
}
|
|
}
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
EM_JS(int, window_inner_width, (), { return window["innerWidth"]; });
|
|
EM_JS(int, window_inner_height, (), { return window["innerHeight"]; });
|
|
EM_JS(char*, get_location_hash_str, (), {
|
|
var jsString = window.location.hash.substring(1);
|
|
var lengthBytes = lengthBytesUTF8(jsString) + 1;
|
|
var stringOnWasmHeap = _malloc(lengthBytes);
|
|
stringToUTF8(jsString, stringOnWasmHeap, lengthBytes);
|
|
return stringOnWasmHeap;
|
|
});
|
|
#endif
|
|
|
|
static void mouse_button_callback(GLFWwindow* window,
|
|
int button,
|
|
int action,
|
|
int mods)
|
|
{
|
|
double x, y;
|
|
glfwGetCursorPos(window, &x, &y);
|
|
float dpiScale = fiddleContext->dpiScale(window);
|
|
x *= dpiScale;
|
|
y *= dpiScale;
|
|
dragLastPos = float2{(float)x, (float)y};
|
|
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
|
|
{
|
|
dragIdx = -1;
|
|
if (!rivFile)
|
|
{
|
|
for (int i = 0; i < NUM_INTERACTIVE_PTS; ++i)
|
|
{
|
|
if (simd::all(simd::abs(dragLastPos - (pts[i] + translate)) <
|
|
100))
|
|
{
|
|
dragIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mousemove_callback(GLFWwindow* window, double x, double y)
|
|
{
|
|
float dpiScale = fiddleContext->dpiScale(window);
|
|
x *= dpiScale;
|
|
y *= dpiScale;
|
|
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
|
|
{
|
|
float2 pos = float2{(float)x, (float)y};
|
|
if (dragIdx >= 0)
|
|
{
|
|
pts[dragIdx] += (pos - dragLastPos);
|
|
}
|
|
else
|
|
{
|
|
translate += (pos - dragLastPos);
|
|
}
|
|
dragLastPos = pos;
|
|
}
|
|
}
|
|
|
|
int lastWidth = 0, lastHeight = 0;
|
|
double fpsLastTime = 0;
|
|
int fpsFrames = 0;
|
|
static bool needsTitleUpdate = false;
|
|
|
|
enum class API
|
|
{
|
|
gl,
|
|
metal,
|
|
d3d,
|
|
d3d12,
|
|
dawn,
|
|
vulkan,
|
|
};
|
|
|
|
API api =
|
|
#if defined(__APPLE__)
|
|
API::metal
|
|
#elif defined(_WIN32)
|
|
API::d3d
|
|
#else
|
|
API::gl
|
|
#endif
|
|
;
|
|
|
|
bool angle = false;
|
|
bool skia = false;
|
|
|
|
static void key_callback(GLFWwindow* window,
|
|
int key,
|
|
int scancode,
|
|
int action,
|
|
int mods)
|
|
{
|
|
bool shift = mods & GLFW_MOD_SHIFT;
|
|
if (action == GLFW_PRESS)
|
|
{
|
|
switch (key)
|
|
{
|
|
case GLFW_KEY_ESCAPE:
|
|
glfwSetWindowShouldClose(window, 1);
|
|
break;
|
|
case GLFW_KEY_GRAVE_ACCENT: // ` key, backtick
|
|
hotloadShaders = true;
|
|
break;
|
|
case GLFW_KEY_A:
|
|
forceAtomicMode = !forceAtomicMode;
|
|
fpsLastTime = 0;
|
|
fpsFrames = 0;
|
|
needsTitleUpdate = true;
|
|
break;
|
|
case GLFW_KEY_D:
|
|
printf("static float scale = %f;\n", scale);
|
|
printf("static float2 translate = {%f, %f};\n",
|
|
translate.x,
|
|
translate.y);
|
|
printf("static float2 pts[] = {");
|
|
for (int i = 0; i < NUM_INTERACTIVE_PTS; i++)
|
|
{
|
|
printf("{%g, %g}", pts[i].x, pts[i].y);
|
|
if (i < NUM_INTERACTIVE_PTS - 1)
|
|
{
|
|
printf(", ");
|
|
}
|
|
else
|
|
{
|
|
printf("};\n");
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
break;
|
|
case GLFW_KEY_Z:
|
|
fiddleContext->toggleZoomWindow();
|
|
break;
|
|
case GLFW_KEY_MINUS:
|
|
strokeWidth /= 1.5f;
|
|
break;
|
|
case GLFW_KEY_EQUAL:
|
|
strokeWidth *= 1.5f;
|
|
break;
|
|
case GLFW_KEY_W:
|
|
wireframe = !wireframe;
|
|
break;
|
|
case GLFW_KEY_C:
|
|
cap = static_cast<StrokeCap>((static_cast<int>(cap) + 1) % 3);
|
|
break;
|
|
case GLFW_KEY_O:
|
|
doClose = !doClose;
|
|
break;
|
|
case GLFW_KEY_S:
|
|
if (shift)
|
|
{
|
|
// Toggle Skia.
|
|
clear_scenes();
|
|
rivFile = nullptr;
|
|
skia = !skia;
|
|
fiddleContext = skia ? FiddleContext::MakeGLSkia()
|
|
: FiddleContext::MakeGLPLS();
|
|
lastWidth = 0;
|
|
lastHeight = 0;
|
|
fpsLastTime = 0;
|
|
fpsFrames = 0;
|
|
needsTitleUpdate = true;
|
|
}
|
|
else
|
|
{
|
|
disableStroke = !disableStroke;
|
|
}
|
|
break;
|
|
case GLFW_KEY_F:
|
|
disableFill = !disableFill;
|
|
break;
|
|
case GLFW_KEY_X:
|
|
clockwiseFill = !clockwiseFill;
|
|
break;
|
|
case GLFW_KEY_P:
|
|
paused = !paused;
|
|
break;
|
|
case GLFW_KEY_H:
|
|
if (!shift)
|
|
++horzRepeat;
|
|
else if (horzRepeat > 0)
|
|
--horzRepeat;
|
|
break;
|
|
case GLFW_KEY_K:
|
|
if (!shift)
|
|
++upRepeat;
|
|
else if (upRepeat > 0)
|
|
--upRepeat;
|
|
break;
|
|
case GLFW_KEY_J:
|
|
if (!rivFile)
|
|
join = static_cast<StrokeJoin>(
|
|
(static_cast<int>(join) + 1) % 3);
|
|
else if (!shift)
|
|
++downRepeat;
|
|
else if (downRepeat > 0)
|
|
--downRepeat;
|
|
break;
|
|
case GLFW_KEY_UP:
|
|
{
|
|
float oldScale = scale;
|
|
scale *= 1.25;
|
|
double x = 0, y = 0;
|
|
glfwGetCursorPos(window, &x, &y);
|
|
float2 cursorPos = float2{(float)x, (float)y} *
|
|
fiddleContext->dpiScale(window);
|
|
translate =
|
|
cursorPos + (translate - cursorPos) * scale / oldScale;
|
|
break;
|
|
}
|
|
case GLFW_KEY_DOWN:
|
|
{
|
|
float oldScale = scale;
|
|
scale /= 1.25;
|
|
double x = 0, y = 0;
|
|
glfwGetCursorPos(window, &x, &y);
|
|
float2 cursorPos = float2{(float)x, (float)y} *
|
|
fiddleContext->dpiScale(window);
|
|
translate =
|
|
cursorPos + (translate - cursorPos) * scale / oldScale;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void glfw_error_callback(int code, const char* message)
|
|
{
|
|
printf("GLFW error: %i - %s\n", code, message);
|
|
}
|
|
|
|
static void set_environment_variable(const char* name, const char* value)
|
|
{
|
|
if (const char* existingValue = getenv(name))
|
|
{
|
|
printf("warning: %s=%s already set. Overriding with %s=%s\n",
|
|
name,
|
|
existingValue,
|
|
name,
|
|
value);
|
|
}
|
|
#ifdef _WIN32
|
|
SetEnvironmentVariableA(name, value);
|
|
#else
|
|
setenv(name, value, /*overwrite=*/true);
|
|
#endif
|
|
}
|
|
|
|
std::unique_ptr<Renderer> renderer;
|
|
std::string rivName;
|
|
|
|
void riveMainLoop();
|
|
|
|
int main(int argc, const char** argv)
|
|
{
|
|
// Cause stdout and stderr to print immediately without buffering.
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
setvbuf(stderr, NULL, _IONBF, 0);
|
|
|
|
#ifdef DEBUG
|
|
options.enableVulkanValidationLayers = true;
|
|
#endif
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
emscripten_set_main_loop(riveMainLoop, 0, false);
|
|
|
|
// Override argc/argv with the window location hash string.
|
|
char* hash = get_location_hash_str();
|
|
std::stringstream ss(hash);
|
|
std::vector<std::string> hashStrs;
|
|
std::vector<const char*> hashArgs;
|
|
std::string arg;
|
|
|
|
hashStrs.push_back("index.html");
|
|
while (std::getline(ss, arg, ':'))
|
|
{
|
|
hashStrs.push_back(std::move(arg));
|
|
}
|
|
for (const std::string& str : hashStrs)
|
|
{
|
|
hashArgs.push_back(str.c_str());
|
|
}
|
|
|
|
argc = hashArgs.size();
|
|
argv = hashArgs.data();
|
|
free(hash);
|
|
#endif
|
|
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (!strcmp(argv[i], "--gl"))
|
|
{
|
|
api = API::gl;
|
|
}
|
|
else if (!strcmp(argv[i], "--glatomic"))
|
|
{
|
|
api = API::gl;
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--glcw"))
|
|
{
|
|
api = API::gl;
|
|
forceAtomicMode = true;
|
|
clockwiseFill = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--metal"))
|
|
{
|
|
api = API::metal;
|
|
}
|
|
else if (!strcmp(argv[i], "--metalcw"))
|
|
{
|
|
api = API::metal;
|
|
clockwiseFill = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--metalatomic"))
|
|
{
|
|
api = API::metal;
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--mvk") || !strcmp(argv[i], "--moltenvk"))
|
|
{
|
|
set_environment_variable("VK_ICD_FILENAMES", kMoltenVKICD);
|
|
api = API::vulkan;
|
|
}
|
|
else if (!strcmp(argv[i], "--mvkatomic") ||
|
|
!strcmp(argv[i], "--moltenvkatomic"))
|
|
{
|
|
set_environment_variable("VK_ICD_FILENAMES", kMoltenVKICD);
|
|
api = API::vulkan;
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--sw") || !strcmp(argv[i], "--swiftshader"))
|
|
{
|
|
// Use the swiftshader built by
|
|
// packages/runtime/renderer/make_swiftshader.sh
|
|
set_environment_variable("VK_ICD_FILENAMES", kSwiftShaderICD);
|
|
api = API::vulkan;
|
|
}
|
|
else if (!strcmp(argv[i], "--swatomic") ||
|
|
!strcmp(argv[i], "--swiftshaderatomic"))
|
|
{
|
|
// Use the swiftshader built by
|
|
// packages/runtime/renderer/make_swiftshader.sh
|
|
set_environment_variable("VK_ICD_FILENAMES", kSwiftShaderICD);
|
|
api = API::vulkan;
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--dawn"))
|
|
{
|
|
api = API::dawn;
|
|
}
|
|
else if (!strcmp(argv[i], "--d3d"))
|
|
{
|
|
api = API::d3d;
|
|
}
|
|
else if (!strcmp(argv[i], "--d3d12"))
|
|
{
|
|
api = API::d3d12;
|
|
}
|
|
else if (!strcmp(argv[i], "--d3datomic"))
|
|
{
|
|
api = API::d3d;
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--d3d12atomic"))
|
|
{
|
|
api = API::d3d12;
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--vulkan") || !strcmp(argv[i], "--vk"))
|
|
{
|
|
api = API::vulkan;
|
|
}
|
|
else if (!strcmp(argv[i], "--vkcw"))
|
|
{
|
|
api = API::vulkan;
|
|
clockwiseFill = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--vulkanatomic") ||
|
|
!strcmp(argv[i], "--vkatomic"))
|
|
{
|
|
api = API::vulkan;
|
|
forceAtomicMode = true;
|
|
}
|
|
#ifdef RIVE_DESKTOP_GL
|
|
else if (!strcmp(argv[i], "--angle_gl"))
|
|
{
|
|
api = API::gl;
|
|
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE,
|
|
GLFW_ANGLE_PLATFORM_TYPE_OPENGL);
|
|
angle = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--angle_d3d"))
|
|
{
|
|
api = API::gl;
|
|
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE,
|
|
GLFW_ANGLE_PLATFORM_TYPE_D3D11);
|
|
angle = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--angle_vk"))
|
|
{
|
|
api = API::gl;
|
|
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE,
|
|
GLFW_ANGLE_PLATFORM_TYPE_VULKAN);
|
|
angle = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--angle_mtl"))
|
|
{
|
|
api = API::gl;
|
|
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE,
|
|
GLFW_ANGLE_PLATFORM_TYPE_METAL);
|
|
angle = true;
|
|
}
|
|
#endif
|
|
else if (!strcmp(argv[i], "--skia"))
|
|
{
|
|
skia = true;
|
|
}
|
|
else if (sscanf(argv[i], "-a%i", &animation))
|
|
{
|
|
// Already updated animation.
|
|
}
|
|
else if (sscanf(argv[i], "-s%i", &stateMachine))
|
|
{
|
|
// Already updated stateMachine.
|
|
}
|
|
else if (sscanf(argv[i], "-h%i", &horzRepeat))
|
|
{
|
|
// Already updated horzRepeat.
|
|
}
|
|
else if (sscanf(argv[i], "-j%i", &downRepeat))
|
|
{
|
|
// Already updated downRepeat.
|
|
}
|
|
else if (sscanf(argv[i], "-k%i", &upRepeat))
|
|
{
|
|
// Already updated upRepeat.
|
|
}
|
|
else if (!strcmp(argv[i], "-p"))
|
|
{
|
|
paused = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--d3d12Warp"))
|
|
{
|
|
options.d3d12UseWarpDevice = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--atomic"))
|
|
{
|
|
forceAtomicMode = true;
|
|
}
|
|
else if (!strncmp(argv[i], "--msaa", 6))
|
|
{
|
|
msaa = argv[i][6] - '0';
|
|
}
|
|
else if (!strcmp(argv[i], "--validation"))
|
|
{
|
|
options.enableVulkanValidationLayers = true;
|
|
}
|
|
else if (!strcmp(argv[i], "--gpu") || !strcmp(argv[i], "-G"))
|
|
{
|
|
options.gpuNameFilter = argv[++i];
|
|
}
|
|
else
|
|
{
|
|
rivName = argv[i];
|
|
}
|
|
}
|
|
|
|
glfwSetErrorCallback(glfw_error_callback);
|
|
|
|
if (!glfwInit())
|
|
{
|
|
fprintf(stderr, "Failed to initialize glfw.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (msaa > 0)
|
|
{
|
|
if (msaa > 1)
|
|
{
|
|
glfwWindowHint(GLFW_SAMPLES, msaa);
|
|
}
|
|
glfwWindowHint(GLFW_STENCIL_BITS, 8);
|
|
glfwWindowHint(GLFW_DEPTH_BITS, 16);
|
|
}
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
|
switch (api)
|
|
{
|
|
case API::metal:
|
|
case API::d3d:
|
|
case API::d3d12:
|
|
case API::dawn:
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
|
|
break;
|
|
case API::vulkan:
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
break;
|
|
case API::gl:
|
|
if (angle)
|
|
{
|
|
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
}
|
|
else
|
|
{
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
|
}
|
|
break;
|
|
}
|
|
glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
|
|
// glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
|
|
window = glfwCreateWindow(1600, 1600, "Rive Renderer", nullptr, nullptr);
|
|
if (!window)
|
|
{
|
|
glfwTerminate();
|
|
fprintf(stderr, "Failed to create window.\n");
|
|
return -1;
|
|
}
|
|
|
|
glfwSetMouseButtonCallback(window, mouse_button_callback);
|
|
glfwSetCursorPosCallback(window, mousemove_callback);
|
|
glfwSetKeyCallback(window, key_callback);
|
|
if (api == API::gl)
|
|
{
|
|
glfwMakeContextCurrent(window);
|
|
}
|
|
glfwShowWindow(window);
|
|
|
|
switch (api)
|
|
{
|
|
case API::metal:
|
|
fiddleContext = FiddleContext::MakeMetalPLS(options);
|
|
break;
|
|
case API::d3d:
|
|
fiddleContext = FiddleContext::MakeD3DPLS(options);
|
|
break;
|
|
case API::d3d12:
|
|
fiddleContext = FiddleContext::MakeD3D12PLS(options);
|
|
break;
|
|
case API::dawn:
|
|
fiddleContext = FiddleContext::MakeDawnPLS(options);
|
|
break;
|
|
case API::vulkan:
|
|
fiddleContext = FiddleContext::MakeVulkanPLS(options);
|
|
break;
|
|
case API::gl:
|
|
fiddleContext =
|
|
skia ? FiddleContext::MakeGLSkia() : FiddleContext::MakeGLPLS();
|
|
break;
|
|
}
|
|
if (!fiddleContext)
|
|
{
|
|
fprintf(stderr, "Failed to create a fiddle context.\n");
|
|
abort();
|
|
}
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
if (api == API::gl)
|
|
{
|
|
glfwSwapInterval(0);
|
|
}
|
|
while (!glfwWindowShouldClose(window))
|
|
{
|
|
riveMainLoop();
|
|
fiddleContext->tick();
|
|
if (api == API::gl)
|
|
{
|
|
glfwSwapBuffers(window);
|
|
}
|
|
if (!rivName.empty())
|
|
{
|
|
glfwPollEvents();
|
|
}
|
|
else
|
|
{
|
|
glfwWaitEvents();
|
|
}
|
|
}
|
|
fiddleContext = nullptr;
|
|
glfwTerminate();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void update_window_title(double fps,
|
|
int instances,
|
|
int width,
|
|
int height)
|
|
{
|
|
std::ostringstream title;
|
|
if (fps != 0)
|
|
{
|
|
title << '[' << fps << " FPS]";
|
|
}
|
|
if (instances > 1)
|
|
{
|
|
title << " (x" << instances << " instances)";
|
|
}
|
|
if (skia)
|
|
{
|
|
title << " | SKIA Renderer";
|
|
}
|
|
else
|
|
{
|
|
title << " | RIVE Renderer";
|
|
}
|
|
if (msaa)
|
|
{
|
|
title << " (msaa" << msaa << ')';
|
|
}
|
|
else if (forceAtomicMode)
|
|
{
|
|
title << " (atomic)";
|
|
}
|
|
title << " | " << width << " x " << height;
|
|
glfwSetWindowTitle(window, title.str().c_str());
|
|
}
|
|
|
|
void riveMainLoop()
|
|
{
|
|
RIVE_PROF_FRAME()
|
|
RIVE_PROF_SCOPE()
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
{
|
|
// Fit the canvas to the browser window size.
|
|
int windowWidth = window_inner_width();
|
|
int windowHeight = window_inner_height();
|
|
double devicePixelRatio = emscripten_get_device_pixel_ratio();
|
|
int canvasExpectedWidth = windowWidth * devicePixelRatio;
|
|
int canvasExpectedHeight = windowHeight * devicePixelRatio;
|
|
int canvasWidth, canvasHeight;
|
|
glfwGetFramebufferSize(window, &canvasWidth, &canvasHeight);
|
|
if (canvasWidth != canvasExpectedWidth ||
|
|
canvasHeight != canvasExpectedHeight)
|
|
{
|
|
glfwSetWindowSize(window,
|
|
canvasExpectedWidth,
|
|
canvasExpectedHeight);
|
|
emscripten_set_element_css_size("#canvas",
|
|
windowWidth,
|
|
windowHeight);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int width = 0, height = 0;
|
|
glfwGetFramebufferSize(window, &width, &height);
|
|
if (lastWidth != width || lastHeight != height)
|
|
{
|
|
printf("size changed to %ix%i\n", width, height);
|
|
lastWidth = width;
|
|
lastHeight = height;
|
|
fiddleContext->onSizeChanged(window, width, height, msaa);
|
|
renderer = fiddleContext->makeRenderer(width, height);
|
|
needsTitleUpdate = true;
|
|
}
|
|
if (needsTitleUpdate)
|
|
{
|
|
update_window_title(0, 1, width, height);
|
|
needsTitleUpdate = false;
|
|
}
|
|
|
|
if (!rivName.empty() && !rivFile)
|
|
{
|
|
std::ifstream rivStream(rivName, std::ios::binary);
|
|
std::vector<uint8_t> rivBytes(std::istreambuf_iterator<char>(rivStream),
|
|
{});
|
|
rivFile = File::import(rivBytes, fiddleContext->factory());
|
|
}
|
|
|
|
// Call right before begin()
|
|
if (hotloadShaders)
|
|
{
|
|
hotloadShaders = false;
|
|
|
|
#ifndef RIVE_BUILD_FOR_IOS
|
|
std::system("sh rebuild_shaders.sh /tmp/rive");
|
|
#endif
|
|
fiddleContext->hotloadShaders();
|
|
}
|
|
fiddleContext->begin({
|
|
.renderTargetWidth = static_cast<uint32_t>(width),
|
|
.renderTargetHeight = static_cast<uint32_t>(height),
|
|
.clearColor = 0xff303030,
|
|
.msaaSampleCount = msaa,
|
|
.disableRasterOrdering = forceAtomicMode,
|
|
.wireframe = wireframe,
|
|
.fillsDisabled = disableFill,
|
|
.strokesDisabled = disableStroke,
|
|
.clockwiseFillOverride = clockwiseFill,
|
|
});
|
|
|
|
int instances = 1;
|
|
if (rivFile)
|
|
{
|
|
instances = (1 + horzRepeat * 2) * (1 + upRepeat + downRepeat);
|
|
if (artboards.size() != instances || scenes.size() != instances)
|
|
{
|
|
make_scenes(instances);
|
|
}
|
|
else if (!paused)
|
|
{
|
|
for (const auto& scene : scenes)
|
|
{
|
|
scene->advanceAndApply(1 / 120.f);
|
|
}
|
|
}
|
|
Mat2D m = computeAlignment(rive::Fit::contain,
|
|
rive::Alignment::center,
|
|
rive::AABB(0, 0, width, height),
|
|
artboards.front()->bounds());
|
|
renderer->save();
|
|
m = Mat2D(scale, 0, 0, scale, translate.x, translate.y) * m;
|
|
renderer->transform(m);
|
|
float spacing = 200 / m.findMaxScale();
|
|
auto scene = scenes.begin();
|
|
for (int j = 0; j < upRepeat + 1 + downRepeat; ++j)
|
|
{
|
|
renderer->save();
|
|
renderer->transform(Mat2D::fromTranslate(-spacing * horzRepeat,
|
|
(j - upRepeat) * spacing));
|
|
for (int i = 0; i < horzRepeat * 2 + 1; ++i)
|
|
{
|
|
|
|
(*scene++)->draw(renderer.get());
|
|
renderer->transform(Mat2D::fromTranslate(spacing, 0));
|
|
}
|
|
renderer->restore();
|
|
}
|
|
renderer->restore();
|
|
}
|
|
else
|
|
{
|
|
float2 p[9];
|
|
for (int i = 0; i < 9; ++i)
|
|
{
|
|
p[i] = pts[i] + translate;
|
|
}
|
|
RawPath rawPath;
|
|
rawPath.moveTo(p[0].x, p[0].y);
|
|
rawPath.cubicTo(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
|
|
float2 c0 = simd::mix(p[3], p[4], float2(2 / 3.f));
|
|
float2 c1 = simd::mix(p[5], p[4], float2(2 / 3.f));
|
|
rawPath.cubicTo(c0.x, c0.y, c1.x, c1.y, p[5].x, p[5].y);
|
|
rawPath.cubicTo(p[6].x, p[6].y, p[7].x, p[7].y, p[8].x, p[8].y);
|
|
if (doClose)
|
|
{
|
|
rawPath.close();
|
|
}
|
|
|
|
Factory* factory = fiddleContext->factory();
|
|
auto path = factory->makeRenderPath(rawPath, FillRule::clockwise);
|
|
|
|
auto fillPaint = factory->makeRenderPaint();
|
|
float feather = powf(1.5f, (1400 - pts[std::size(pts) - 1].y) / 75);
|
|
if (feather > 1)
|
|
{
|
|
fillPaint->feather(feather);
|
|
}
|
|
fillPaint->color(0xd0ffffff);
|
|
|
|
renderer->drawPath(path.get(), fillPaint.get());
|
|
|
|
if (!disableStroke)
|
|
{
|
|
auto strokePaint = factory->makeRenderPaint();
|
|
strokePaint->style(RenderPaintStyle::stroke);
|
|
strokePaint->color(0x8000ffff);
|
|
strokePaint->thickness(strokeWidth);
|
|
if (feather > 1)
|
|
{
|
|
strokePaint->feather(feather);
|
|
}
|
|
|
|
strokePaint->join(join);
|
|
strokePaint->cap(cap);
|
|
renderer->drawPath(path.get(), strokePaint.get());
|
|
|
|
// Draw the interactive points.
|
|
auto pointPaint = factory->makeRenderPaint();
|
|
pointPaint->style(RenderPaintStyle::stroke);
|
|
pointPaint->color(0xff0000ff);
|
|
pointPaint->thickness(14);
|
|
pointPaint->cap(StrokeCap::round);
|
|
pointPaint->join(StrokeJoin::round);
|
|
|
|
auto pointPath = factory->makeEmptyRenderPath();
|
|
for (int i : {1, 2, 4, 6, 7})
|
|
{
|
|
float2 pt = pts[i] + translate;
|
|
pointPath->moveTo(pt.x, pt.y);
|
|
pointPath->close();
|
|
}
|
|
renderer->drawPath(pointPath.get(), pointPaint.get());
|
|
|
|
// Draw the feather control point.
|
|
pointPaint->color(0xffff0000);
|
|
pointPath = factory->makeEmptyRenderPath();
|
|
float2 pt = pts[std::size(pts) - 1] + translate;
|
|
pointPath->moveTo(pt.x, pt.y);
|
|
renderer->drawPath(pointPath.get(), pointPaint.get());
|
|
}
|
|
}
|
|
|
|
fiddleContext->end(window);
|
|
|
|
if (rivFile)
|
|
{
|
|
// Count FPS.
|
|
++fpsFrames;
|
|
double time = glfwGetTime();
|
|
double fpsElapsed = time - fpsLastTime;
|
|
if (fpsElapsed > 2)
|
|
{
|
|
int instances = (1 + horzRepeat * 2) * (1 + upRepeat + downRepeat);
|
|
double fps = fpsLastTime == 0 ? 0 : fpsFrames / fpsElapsed;
|
|
update_window_title(fps, instances, width, height);
|
|
fpsFrames = 0;
|
|
fpsLastTime = time;
|
|
}
|
|
}
|
|
}
|