bindings/wasm: Support WebGPU

Updated WASM binding to bring WebGPU on the web player.

binding will generate a WASM binary, which can render animation through both SW and WG raster engine.

A raster engine will be chosen by parameter `engine`, when initializing.
This commit is contained in:
Jinny You 2024-08-20 16:58:11 +09:00 committed by Hermet Park
parent 32954f962d
commit d704a2503a
5 changed files with 111 additions and 47 deletions

View file

@ -12,11 +12,10 @@ exe_suffix = 'js'
[built-in options]
cpp_args = ['-Wshift-negative-value', '-flto', '-Os', '-fno-exceptions']
cpp_link_args = ['-Wshift-negative-value', '-flto', '-Os', '-fno-exceptions', '--bind', '-sWASM=1', '-sALLOW_MEMORY_GROWTH=1', '-sEXPORT_ES6=1', '-sFORCE_FILESYSTEM=1', '-sMODULARIZE=1', '-sEXPORTED_RUNTIME_METHODS=FS', '-sSINGLE_FILE=1']
#cpp_link_args = ['-Wshift-negative-value', '-flto', '-Os', '-fno-exceptions', '--bind', '-sWASM=1', '-sALLOW_MEMORY_GROWTH=1', '-sFORCE_FILESYSTEM=1', '-g'] //enable g flag for wasm debugging
cpp_link_args = ['-Wshift-negative-value', '-flto', '-Os', '-fno-exceptions', '--bind', '-sWASM=1', '-sALLOW_MEMORY_GROWTH=1', '-sEXPORT_ES6=1', '-sFORCE_FILESYSTEM=1', '-sMODULARIZE=1', '-sEXPORTED_RUNTIME_METHODS=FS']
[host_machine]
system = 'emscripten'
cpu_family = 'x86'
cpu = 'i686'
cpu_family = 'wasm32'
cpu = 'wasm32'
endian = 'little'

View file

@ -12,7 +12,7 @@ exe_suffix = 'js'
[built-in options]
cpp_args = ['-Wshift-negative-value', '-flto', '-Os', '-fno-exceptions']
cpp_link_args = ['-lembind', '-sMODULARIZE=1', '-sUSE_WEBGPU=1', '-sASYNCIFY=1', '-sFORCE_FILESYSTEM=1', '-sALLOW_MEMORY_GROWTH=1', '-sEXPORT_ES6=1', '-sSINGLE_FILE=1']
cpp_link_args = ['-Wshift-negative-value', '-flto', '-Os', '-fno-exceptions', '--bind', '-sWASM=1', '-sALLOW_MEMORY_GROWTH=1', '-sEXPORT_ES6=1', '-sFORCE_FILESYSTEM=1', '-sMODULARIZE=1', '-sEXPORTED_RUNTIME_METHODS=FS', '-sUSE_WEBGPU=1', '-sASYNCIFY=1']
[host_machine]
system = 'emscripten'

View file

@ -23,6 +23,9 @@
#include <thorvg.h>
#include <emscripten/bind.h>
#include "tvgPicture.h"
#ifdef THORVG_WG_RASTER_SUPPORT
#include <webgpu/webgpu.h>
#endif
using namespace emscripten;
using namespace std;
@ -35,13 +38,60 @@ class __attribute__((visibility("default"))) TvgLottieAnimation
public:
~TvgLottieAnimation()
{
if (engine == tvg::CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
free(buffer);
#endif
} else if (engine == tvg::CanvasEngine::Wg) {
#ifdef THORVG_WG_RASTER_SUPPORT
wgpuSurfaceRelease(surface);
wgpuInstanceRelease(instance);
#endif
}
Initializer::term();
}
static TvgLottieAnimation* create()
explicit TvgLottieAnimation(string engine = "sw", string selector = "")
{
return new TvgLottieAnimation;
errorMsg = NoError;
if (engine == "sw") {
#ifdef THORVG_SW_RASTER_SUPPORT
this->engine = tvg::CanvasEngine::Sw;
if (Initializer::init(0, this->engine) != Result::Success) {
errorMsg = "init() fail";
return;
}
canvas = SwCanvas::gen();
#endif
} else if (engine == "wg") {
#ifdef THORVG_WG_RASTER_SUPPORT
this->engine = tvg::CanvasEngine::Wg;
if (Initializer::init(0, this->engine) != Result::Success) {
errorMsg = "init() fail";
return;
}
//Init WebGPU
instance = wgpuCreateInstance(nullptr);
WGPUSurfaceDescriptorFromCanvasHTMLSelector canvasDesc{};
canvasDesc.chain.next = nullptr;
canvasDesc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
canvasDesc.selector = selector.c_str();
WGPUSurfaceDescriptor surfaceDesc{};
surfaceDesc.nextInChain = &canvasDesc.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDesc);
canvas = WgCanvas::gen();
#endif
}
if (!canvas) errorMsg = "Invalid canvas";
animation = Animation::gen();
if (!animation) errorMsg = "Invalid animation";
}
string error()
@ -85,8 +135,7 @@ public:
return false;
}
//back up for saving
this->data = data;
this->data = data; //back up for saving
canvas->clear(true);
@ -126,7 +175,9 @@ public:
if (!canvas || !animation) return val(typed_memory_view<uint8_t>(0, nullptr));
if (!updated) return val(typed_memory_view(width * height * 4, buffer));
if (!updated) {
return output();
}
if (canvas->draw() != Result::Success) {
errorMsg = "draw() fail";
@ -137,7 +188,7 @@ public:
updated = false;
return val(typed_memory_view(width * height * 4, buffer));
return output();
}
bool update()
@ -186,9 +237,17 @@ public:
this->width = width;
this->height = height;
if (engine == tvg::CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
free(buffer);
buffer = (uint8_t*)malloc(width * height * sizeof(uint32_t));
canvas->target((uint32_t *)buffer, width, width, height, SwCanvas::ABGR8888S);
static_cast<SwCanvas*>(canvas.get())->target((uint32_t *)buffer, width, width, height, SwCanvas::ABGR8888S);
#endif
} else if (engine == tvg::CanvasEngine::Wg) {
#ifdef THORVG_WG_RASTER_SUPPORT
static_cast<WgCanvas*>(canvas.get())->target(this->instance, this->surface, width, height);
#endif
}
float scale;
float shiftX = 0.0f, shiftY = 0.0f;
@ -311,38 +370,39 @@ public:
// TODO: Advanced APIs wrt Interactivity & theme methods...
private:
explicit TvgLottieAnimation()
val output()
{
errorMsg = NoError;
if (Initializer::init(0) != Result::Success) {
errorMsg = "init() fail";
return;
if (engine == tvg::CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
return val(typed_memory_view(width * height * 4, buffer));
#endif
}
canvas = SwCanvas::gen();
if (!canvas) errorMsg = "Invalid canvas";
animation = Animation::gen();
if (!animation) errorMsg = "Invalid animation";
return val(typed_memory_view<uint8_t>(0, nullptr));
}
private:
string errorMsg;
unique_ptr<SwCanvas> canvas = nullptr;
CanvasEngine engine;
unique_ptr<Canvas> canvas = nullptr;
unique_ptr<Animation> animation = nullptr;
string data;
uint8_t* buffer = nullptr;
uint32_t width = 0;
uint32_t height = 0;
float psize[2]; //picture size
bool updated = false;
#ifdef THORVG_SW_RASTER_SUPPORT
uint8_t* buffer = nullptr;
#endif
#ifdef THORVG_WG_RASTER_SUPPORT
WGPUInstance instance{};
WGPUSurface surface{};
#endif
};
EMSCRIPTEN_BINDINGS(thorvg_bindings)
{
class_<TvgLottieAnimation>("TvgLottieAnimation")
.constructor(&TvgLottieAnimation ::create)
.constructor<string, string>()
.function("error", &TvgLottieAnimation ::error, allow_raw_pointers())
.function("size", &TvgLottieAnimation ::size)
.function("duration", &TvgLottieAnimation ::duration)

View file

@ -2,10 +2,26 @@
#https://github.com/thorvg/thorvg/wiki/ThorVG-Viewer-Development-Guide
if [ ! -d "./build_wasm" ]; then
sed "s|EMSDK:|$1|g" ./cross/wasm_x86_i686.txt > /tmp/.wasm_cross.txt
meson -Db_lto=true -Ddefault_library=static -Dstatic=true -Dloaders="all" -Dsavers="all" -Dthreads=false -Dbindings="wasm_beta" --cross-file /tmp/.wasm_cross.txt build_wasm
BACKEND="$1"
EMSDK="$2"
if [ -z "$2" ]; then
BACKEND="all"
EMSDK="$1"
fi
ninja -C build_wasm/
ls -lrt build_wasm/src/bindings/wasm/thorvg-wasm.*
if [ ! -d "./build_wasm32_$BACKEND" ]; then
if [[ "$BACKEND" == "wg" ]]; then
sed "s|EMSDK:|$EMSDK|g" ./cross/wasm32_wg.txt > /tmp/.wasm_cross.txt
meson -Db_lto=true -Ddefault_library=static -Dstatic=true -Dloaders="all" -Dsavers="all" -Dthreads=false -Dbindings="wasm_beta" -Dengines="wg_beta" --cross-file /tmp/.wasm_cross.txt build_wasm32_wg
elif [[ "$BACKEND" == "sw" ]]; then
sed "s|EMSDK:|$EMSDK|g" ./cross/wasm32_sw.txt > /tmp/.wasm_cross.txt
meson -Db_lto=true -Ddefault_library=static -Dstatic=true -Dloaders="all" -Dsavers="all" -Dthreads=false -Dbindings="wasm_beta" --cross-file /tmp/.wasm_cross.txt build_wasm32_sw
else
sed "s|EMSDK:|$EMSDK|g" ./cross/wasm32_wg.txt > /tmp/.wasm_cross.txt
meson -Db_lto=true -Ddefault_library=static -Dstatic=true -Dloaders="all" -Dsavers="all" -Dthreads=false -Dbindings="wasm_beta" -Dengines="wg_beta, sw" --cross-file /tmp/.wasm_cross.txt build_wasm32_all
fi
fi
ninja -C build_wasm32_$BACKEND/
ls -lrt build_wasm32_$BACKEND/src/bindings/wasm/*.{js,wasm}

View file

@ -1,11 +0,0 @@
#!/bin/bash
#https://github.com/thorvg/thorvg/wiki/WebGPU-Raster-Engine-Development
if [ ! -d "./build_wasm" ]; then
sed "s|EMSDK:|$1|g" ./cross/wasm_webgpu.txt > /tmp/.wasm_cross.txt
meson -Db_lto=true -Ddefault_library=static -Dstatic=true -Dloaders="all" -Dsavers="all" -Dthreads=false -Dbindings="wasm_beta" -Dengines="wg_beta" --cross-file /tmp/.wasm_cross.txt build_wasm
fi
ninja -C build_wasm/
ls -lrt build_wasm/src/bindings/wasm/thorvg-wasm.*