| /* |
| * Copyright 2023 Rive |
| */ |
| |
| #include "rive/artboard.hpp" |
| #include "rive/file.hpp" |
| #include "rive/layout.hpp" |
| #include "rive/animation/state_machine_instance.hpp" |
| |
| #include <iterator> |
| #include <vector> |
| |
| using namespace rive; |
| |
| #ifdef RIVE_WEBGPU |
| #include "rive/renderer/rive_renderer.hpp" |
| #include "rive/renderer/webgpu/render_context_webgpu_impl.hpp" |
| |
| #include "../src/webgpu/webgpu_compat.h" |
| #include "marty.h" |
| #include "egg_v2.h" |
| #include "rope.h" |
| |
| #include <webgpu/webgpu_cpp.h> |
| #include <emscripten.h> |
| #include <emscripten/html5_webgpu.h> |
| #include <emscripten/html5.h> |
| |
| using namespace rive::gpu; |
| |
| static std::unique_ptr<RenderContext> renderContext; |
| static rcp<RenderTargetWebGPU> renderTarget; |
| static std::unique_ptr<Renderer> renderer; |
| |
| static WGPUInstance instance; |
| static wgpu::Adapter adapter; |
| static wgpu::Device device; |
| static WGPUSurface surface; |
| static WGPUTextureFormat format = WGPUTextureFormat_Undefined; |
| static wgpu::Queue queue; |
| |
| static std::unique_ptr<File> rivFile; |
| static std::unique_ptr<ArtboardInstance> artboard; |
| static std::unique_ptr<Scene> scene; |
| |
| extern "C" EM_BOOL animationFrame(double time, void* userData); |
| extern "C" void start(void); |
| |
| static void requestDeviceCallback(WGPURequestDeviceStatus status, |
| WGPUDevice deviceArg, |
| const char* message, |
| void* userdata); |
| static void requestAdapterCallback(WGPURequestAdapterStatus status, |
| WGPUAdapter adapterArg, |
| const char* message, |
| void* userdata); |
| |
| void requestDeviceCallback(WGPURequestDeviceStatus status, |
| WGPUDevice deviceArg, |
| const char* message, |
| void* userdata) |
| { |
| assert(userdata == instance); |
| |
| device = wgpu::Device::Acquire(deviceArg); |
| assert(device.Get()); |
| |
| queue = device.GetQueue(); |
| assert(queue.Get()); |
| |
| RenderContextWebGPUImpl::ContextOptions contextOptions; |
| renderContext = RenderContextWebGPUImpl::MakeContext(adapter, |
| device, |
| queue, |
| contextOptions); |
| renderTarget = |
| renderContext->static_impl_cast<RenderContextWebGPUImpl>() |
| ->makeRenderTarget(wgpu::TextureFormat::RGBA8Unorm, 1920, 1080); |
| renderer = std::make_unique<RiveRenderer>(renderContext.get()); |
| |
| rivFile = File::import({marty, marty_len}, renderContext.get()); |
| // rivFile = File::import({egg_v2, egg_v2_len}, renderContext.get()); |
| // rivFile = File::import({rope, rope_len}, renderContext.get()); |
| artboard = rivFile->artboardDefault(); |
| scene = artboard->defaultScene(); |
| scene->advanceAndApply(0); |
| |
| { |
| WGPUSurfaceDescriptorFromCanvasHTMLSelector htmlSelector = {}; |
| htmlSelector.chain.sType = |
| WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector; |
| htmlSelector.selector = "#canvas"; |
| |
| WGPUSurfaceDescriptor surfaceDesc = WGPU_SURFACE_DESCRIPTOR_INIT; |
| surfaceDesc.nextInChain = (WGPUChainedStruct*)&htmlSelector; |
| |
| surface = wgpuInstanceCreateSurface(instance, &surfaceDesc); |
| assert(surface); |
| } |
| |
| { |
| format = wgpuSurfaceGetPreferredFormat(surface, adapter.Get()); |
| assert(format); |
| } |
| |
| { |
| WGPUSurfaceConfiguration conf = WGPU_SURFACE_CONFIGURATION_INIT; |
| conf.device = device.Get(); |
| conf.format = format; |
| |
| wgpuSurfaceConfigure(surface, &conf); |
| } |
| |
| emscripten_set_canvas_element_size("#canvas", 1920, 1080); |
| |
| emscripten_request_animation_frame_loop(animationFrame, (void*)100); |
| } |
| |
| void requestAdapterCallback(WGPURequestAdapterStatus status, |
| WGPUAdapter adapterArg, |
| const char* message, |
| void* userdata) |
| { |
| assert(adapterArg); |
| assert(status == WGPURequestAdapterStatus_Success); |
| assert(userdata == instance); |
| adapter = wgpu::Adapter::Acquire(adapterArg); |
| adapter.RequestDevice({}, requestDeviceCallback, userdata); |
| } |
| |
| static double lastTime = 0; |
| extern "C" EM_BOOL animationFrame(double time, void* userData) |
| { |
| (void)time; |
| (void)userData; |
| |
| WGPUSurfaceTexture surfaceTexture = WGPU_SURFACE_TEXTURE_INIT; |
| |
| wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture); |
| |
| WGPUTexture texture = surfaceTexture.texture; |
| |
| WGPUTextureViewDescriptor textureViewDesc = |
| WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT; |
| textureViewDesc.format = format; |
| textureViewDesc.dimension = WGPUTextureViewDimension_2D; |
| |
| WGPUTextureView textureView = |
| wgpuTextureCreateView(texture, &textureViewDesc); |
| |
| scene->advanceAndApply(lastTime == 0 ? 0 : (time - lastTime) * 1e-3f); |
| lastTime = time; |
| |
| renderContext->beginFrame({ |
| .renderTargetWidth = renderTarget->width(), |
| .renderTargetHeight = renderTarget->height(), |
| .loadAction = gpu::LoadAction::clear, |
| .clearColor = 0xff8030ff, |
| }); |
| |
| renderer->save(); |
| renderer->transform(computeAlignment(rive::Fit::contain, |
| rive::Alignment::center, |
| rive::AABB(0, 0, 1920, 1080), |
| artboard->bounds())); |
| scene->draw(renderer.get()); |
| renderer->restore(); |
| |
| renderTarget->setTargetTextureView(textureView); |
| renderContext->flush({.renderTarget = renderTarget.get()}); |
| |
| wgpuTextureViewRelease(textureView); |
| wgpuTextureRelease(texture); |
| |
| return EM_TRUE; |
| } |
| |
| int main(void) |
| { |
| instance = wgpuCreateInstance(NULL); |
| assert(instance); |
| |
| wgpuInstanceRequestAdapter(instance, |
| NULL, |
| requestAdapterCallback, |
| instance); |
| |
| return 0; |
| } |
| |
| extern "C" void start(void) { main(); } |
| |
| #endif |
| |
| #ifdef RIVE_DAWN |
| |
| #include "../path_fiddle/fiddle_context.hpp" |
| |
| #include <dawn/webgpu_cpp.h> |
| #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/poison_loader.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 |