mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Update Dawn integration and peg the build script at a specific commit. Implement feathering. Fix image textures. Diffs= 3eae4e45b0 Fix bit rotting in WebGPU (#9183) Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
444 lines
16 KiB
C++
444 lines
16 KiB
C++
/*
|
|
* Copyright 2023 Rive
|
|
*/
|
|
|
|
#include "rive/math/simd.hpp"
|
|
#include "rive/artboard.hpp"
|
|
#include "rive/file.hpp"
|
|
#include "rive/layout.hpp"
|
|
#include "rive/animation/linear_animation_instance.hpp"
|
|
#include "rive/animation/state_machine_input_instance.hpp"
|
|
#include "rive/animation/state_machine_instance.hpp"
|
|
#include "rive/renderer/render_context.hpp"
|
|
#include "rive/renderer/rive_renderer.hpp"
|
|
#include "rive/renderer/webgpu/render_context_webgpu_impl.hpp"
|
|
#include "rive/renderer/webgpu/em_js_handle.hpp"
|
|
|
|
#include <cmath>
|
|
#include <iterator>
|
|
#include <vector>
|
|
#include <filesystem>
|
|
|
|
#ifdef RIVE_WEBGPU
|
|
#include <webgpu/webgpu_cpp.h>
|
|
#include <emscripten.h>
|
|
#include <emscripten/html5_webgpu.h>
|
|
#else
|
|
#include <dawn/webgpu_cpp.h>
|
|
#include "dawn/native/DawnNative.h"
|
|
#include "dawn/dawn_proc.h"
|
|
#define EMSCRIPTEN_KEEPALIVE
|
|
#endif
|
|
|
|
using namespace rive;
|
|
using namespace rive::gpu;
|
|
using PixelLocalStorageType = RenderContextWebGPUImpl::PixelLocalStorageType;
|
|
|
|
#ifdef RIVE_WEBGPU
|
|
static std::unique_ptr<RenderContext> s_renderContext;
|
|
static rcp<RenderTargetWebGPU> s_renderTarget;
|
|
static std::unique_ptr<Renderer> s_renderer;
|
|
|
|
void riveInitPlayer(int w,
|
|
int h,
|
|
bool invertRenderTargetFrontFace,
|
|
wgpu::Device gpu,
|
|
wgpu::Queue queue,
|
|
PixelLocalStorageType plsType,
|
|
int maxVertexStorageBlocks)
|
|
{
|
|
RenderContextWebGPUImpl::ContextOptions contextOptions = {
|
|
.plsType = plsType,
|
|
.disableStorageBuffers =
|
|
maxVertexStorageBlocks < gpu::kMaxStorageBuffers,
|
|
.invertRenderTargetY = h < 0,
|
|
.invertRenderTargetFrontFace = invertRenderTargetFrontFace,
|
|
};
|
|
s_renderContext =
|
|
RenderContextWebGPUImpl::MakeContext(gpu, queue, contextOptions);
|
|
s_renderTarget =
|
|
s_renderContext->static_impl_cast<RenderContextWebGPUImpl>()
|
|
->makeRenderTarget(wgpu::TextureFormat::BGRA8Unorm, w, abs(h));
|
|
s_renderer = std::make_unique<RiveRenderer>(s_renderContext.get());
|
|
}
|
|
|
|
EmJsHandle s_deviceHandle;
|
|
EmJsHandle s_queueHandle;
|
|
EmJsHandle s_textureViewHandle;
|
|
|
|
extern "C"
|
|
{
|
|
void EMSCRIPTEN_KEEPALIVE RiveInitialize(int deviceID,
|
|
int queueID,
|
|
int canvasWidth,
|
|
int canvasHeight,
|
|
bool invertRenderTargetFrontFace,
|
|
int pixelLocalStorageType,
|
|
int maxVertexStorageBlocks)
|
|
{
|
|
s_deviceHandle = EmJsHandle(deviceID);
|
|
s_queueHandle = EmJsHandle(queueID);
|
|
riveInitPlayer(
|
|
canvasWidth,
|
|
canvasHeight,
|
|
invertRenderTargetFrontFace,
|
|
wgpu::Device::Acquire(
|
|
emscripten_webgpu_import_device(s_deviceHandle.get())),
|
|
emscripten_webgpu_import_queue(s_queueHandle.get()),
|
|
static_cast<PixelLocalStorageType>(pixelLocalStorageType),
|
|
maxVertexStorageBlocks);
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE RiveBeginRendering(int textureViewID,
|
|
int loadAction,
|
|
ColorInt clearColor)
|
|
{
|
|
s_textureViewHandle = EmJsHandle(textureViewID);
|
|
auto targetTextureView = wgpu::TextureView::Acquire(
|
|
emscripten_webgpu_import_texture_view(s_textureViewHandle.get()));
|
|
s_renderTarget->setTargetTextureView(targetTextureView);
|
|
|
|
s_renderContext->beginFrame({
|
|
.renderTargetWidth = s_renderTarget->width(),
|
|
.renderTargetHeight = s_renderTarget->height(),
|
|
.loadAction = static_cast<gpu::LoadAction>(loadAction),
|
|
.clearColor = clearColor,
|
|
});
|
|
|
|
s_renderer->save();
|
|
return reinterpret_cast<intptr_t>(s_renderer.get());
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE RiveFlushRendering()
|
|
{
|
|
s_renderer->restore();
|
|
s_renderContext->flush({.renderTarget = s_renderTarget.get()});
|
|
s_textureViewHandle = EmJsHandle();
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE RiveLoadFile(intptr_t wasmBytesPtr,
|
|
size_t length)
|
|
{
|
|
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(wasmBytesPtr);
|
|
std::unique_ptr<File> file =
|
|
File::import({bytes, length}, s_renderContext.get());
|
|
return reinterpret_cast<intptr_t>(file.release());
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE File_artboardNamed(intptr_t nativePtr,
|
|
const char* name)
|
|
{
|
|
auto file = reinterpret_cast<File*>(nativePtr);
|
|
std::unique_ptr<ArtboardInstance> artboard = file->artboardNamed(name);
|
|
return reinterpret_cast<intptr_t>(artboard.release());
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE File_artboardDefault(intptr_t nativePtr)
|
|
{
|
|
auto file = reinterpret_cast<File*>(nativePtr);
|
|
std::unique_ptr<ArtboardInstance> artboard = file->artboardDefault();
|
|
return reinterpret_cast<intptr_t>(artboard.release());
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE File_destroy(intptr_t nativePtr)
|
|
{
|
|
auto file = reinterpret_cast<File*>(nativePtr);
|
|
delete file;
|
|
}
|
|
|
|
int EMSCRIPTEN_KEEPALIVE ArtboardInstance_width(intptr_t nativePtr)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
return artboard->bounds().width();
|
|
}
|
|
|
|
int EMSCRIPTEN_KEEPALIVE ArtboardInstance_height(intptr_t nativePtr)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
return artboard->bounds().height();
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE
|
|
ArtboardInstance_stateMachineNamed(intptr_t nativePtr, const char* name)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
std::unique_ptr<StateMachineInstance> stateMachine =
|
|
artboard->stateMachineNamed(name);
|
|
return reinterpret_cast<intptr_t>(stateMachine.release());
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE
|
|
ArtboardInstance_animationNamed(intptr_t nativePtr, const char* name)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
std::unique_ptr<LinearAnimationInstance> animation =
|
|
artboard->animationNamed(name);
|
|
return reinterpret_cast<intptr_t>(animation.release());
|
|
}
|
|
|
|
intptr_t EMSCRIPTEN_KEEPALIVE
|
|
ArtboardInstance_defaultStateMachine(intptr_t nativePtr)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
std::unique_ptr<StateMachineInstance> stateMachine =
|
|
artboard->defaultStateMachine();
|
|
return reinterpret_cast<intptr_t>(stateMachine.release());
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE ArtboardInstance_align(intptr_t nativePtr,
|
|
intptr_t rendererNativePtr,
|
|
float frameLeft,
|
|
float frameTop,
|
|
float frameRight,
|
|
float frameBot,
|
|
int fitValue,
|
|
float alignmentX,
|
|
float alignmentY)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(rendererNativePtr);
|
|
auto fit = static_cast<Fit>(fitValue);
|
|
Alignment alignment = {alignmentX, alignmentY};
|
|
AABB frame = {frameLeft, frameTop, frameRight, frameBot};
|
|
renderer->align(fit, alignment, frame, artboard->bounds());
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE ArtboardInstance_destroy(intptr_t nativePtr)
|
|
{
|
|
auto artboard = reinterpret_cast<ArtboardInstance*>(nativePtr);
|
|
delete artboard;
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_setBool(intptr_t nativePtr,
|
|
const char* inputName,
|
|
int value)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
if (SMIBool* input = stateMachine->getBool(inputName))
|
|
{
|
|
input->value(static_cast<bool>(value));
|
|
}
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_setNumber(intptr_t nativePtr,
|
|
const char* inputName,
|
|
float value)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
if (SMINumber* input = stateMachine->getNumber(inputName))
|
|
{
|
|
input->value(value);
|
|
}
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_fireTrigger(intptr_t nativePtr, const char* inputName)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
if (SMITrigger* input = stateMachine->getTrigger(inputName))
|
|
{
|
|
input->fire();
|
|
}
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_pointerDown(intptr_t nativePtr, float x, float y)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
stateMachine->pointerDown({x, y});
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_pointerMove(intptr_t nativePtr, float x, float y)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
stateMachine->pointerMove({x, y});
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE StateMachineInstance_pointerUp(intptr_t nativePtr,
|
|
float x,
|
|
float y)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
stateMachine->pointerUp({x, y});
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_advanceAndApply(intptr_t nativePtr, double elapsed)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
stateMachine->advanceAndApply(elapsed);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
StateMachineInstance_draw(intptr_t nativePtr, intptr_t rendererNativePtr)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(rendererNativePtr);
|
|
stateMachine->draw(renderer);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE StateMachineInstance_destroy(intptr_t nativePtr)
|
|
{
|
|
auto stateMachine = reinterpret_cast<StateMachineInstance*>(nativePtr);
|
|
delete stateMachine;
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
LinearAnimationInstance_advanceAndApply(intptr_t nativePtr, double elapsed)
|
|
{
|
|
auto animation = reinterpret_cast<LinearAnimationInstance*>(nativePtr);
|
|
animation->advanceAndApply(elapsed);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
LinearAnimationInstance_draw(intptr_t nativePtr, intptr_t rendererNativePtr)
|
|
{
|
|
auto animation = reinterpret_cast<LinearAnimationInstance*>(nativePtr);
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(rendererNativePtr);
|
|
animation->draw(renderer);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE
|
|
LinearAnimationInstance_destroy(intptr_t nativePtr)
|
|
{
|
|
auto animation = reinterpret_cast<LinearAnimationInstance*>(nativePtr);
|
|
delete animation;
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE Renderer_save(intptr_t nativePtr)
|
|
{
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(nativePtr);
|
|
renderer->save();
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE Renderer_restore(intptr_t nativePtr)
|
|
{
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(nativePtr);
|
|
renderer->restore();
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE Renderer_translate(intptr_t nativePtr,
|
|
float x,
|
|
float y)
|
|
{
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(nativePtr);
|
|
renderer->translate(x, y);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE Renderer_transform(intptr_t nativePtr,
|
|
float xx,
|
|
float xy,
|
|
float yx,
|
|
float yy,
|
|
float tx,
|
|
float ty)
|
|
{
|
|
auto renderer = reinterpret_cast<RiveRenderer*>(nativePtr);
|
|
Mat2D matrix(xx, xy, yx, yy, tx, ty);
|
|
renderer->transform(matrix);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef RIVE_DAWN
|
|
#include "../path_fiddle/fiddle_context.hpp"
|
|
#include <GLFW/glfw3.h>
|
|
#define GLFW_EXPOSE_NATIVE_WIN32
|
|
#include <GLFW/glfw3native.h>
|
|
|
|
#include <fstream>
|
|
|
|
static GLFWwindow* window = nullptr;
|
|
static std::unique_ptr<FiddleContext> fiddleContextDawn;
|
|
|
|
static void glfw_error_callback(int code, const char* message)
|
|
{
|
|
printf("GLFW error: %i - %s\n", code, message);
|
|
}
|
|
|
|
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);
|
|
|
|
glfwSetErrorCallback(glfw_error_callback);
|
|
|
|
if (!glfwInit())
|
|
{
|
|
fprintf(stderr, "Failed to initialize glfw.\n");
|
|
return 1;
|
|
}
|
|
|
|
glfwWindowHint(GLFW_SAMPLES, 0);
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
|
|
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;
|
|
}
|
|
glfwSetWindowTitle(window, "Rive Renderer");
|
|
|
|
std::unique_ptr<FiddleContext> fiddleContextDawn =
|
|
FiddleContext::MakeDawnPLS({});
|
|
|
|
int w, h;
|
|
glfwGetFramebufferSize(window, &w, &h);
|
|
fiddleContextDawn->onSizeChanged(window, w, h, 1);
|
|
std::unique_ptr<Renderer> renderer = fiddleContextDawn->makeRenderer(w, h);
|
|
|
|
const char* filename = argc > 1 ? argv[1] : "webgpu_player/rivs/tape.riv";
|
|
std::ifstream rivStream(filename, std::ios::binary);
|
|
std::vector<uint8_t> rivBytes(std::istreambuf_iterator<char>(rivStream),
|
|
{});
|
|
std::unique_ptr<File> rivFile =
|
|
File::import(rivBytes, fiddleContextDawn->factory());
|
|
std::unique_ptr<ArtboardInstance> artboard = rivFile->artboardDefault();
|
|
std::unique_ptr<Scene> scene = artboard->defaultScene();
|
|
scene->advanceAndApply(0);
|
|
|
|
double lastTimestamp = 0;
|
|
|
|
while (!glfwWindowShouldClose(window))
|
|
{
|
|
double timestamp = glfwGetTime();
|
|
|
|
fiddleContextDawn->begin({
|
|
.renderTargetWidth = static_cast<uint32_t>(w),
|
|
.renderTargetHeight = static_cast<uint32_t>(h),
|
|
.clearColor = 0xff8030ff,
|
|
});
|
|
|
|
renderer->save();
|
|
renderer->transform(computeAlignment(rive::Fit::contain,
|
|
rive::Alignment::center,
|
|
rive::AABB(0, 0, w, h),
|
|
artboard->bounds()));
|
|
scene->draw(renderer.get());
|
|
renderer->restore();
|
|
|
|
fiddleContextDawn->end(window);
|
|
fiddleContextDawn->tick();
|
|
glfwPollEvents();
|
|
|
|
if (lastTimestamp != 0)
|
|
{
|
|
scene->advanceAndApply(timestamp - lastTimestamp);
|
|
}
|
|
lastTimestamp = timestamp;
|
|
}
|
|
glfwTerminate();
|
|
return 0;
|
|
}
|
|
#endif
|