Pass struct to wasm (rust)

This commit is contained in:
Belén Albeza
2024-10-04 12:50:16 +02:00
parent c7f801dd44
commit fa9004d12c
5 changed files with 122 additions and 80 deletions

View File

@@ -1,3 +0,0 @@
#!/bin/bash
EMCC_CFLAGS="--no-entry -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s MAX_WEBGL_VERSION=2 -s MODULARIZE=1 -s EXPORT_NAME=createRustSkiaModule -s EXPORTED_RUNTIME_METHODS=GL -s ENVIRONMENT=webgl" cargo build --target=wasm32-unknown-emscripten

6
frontend/render_v2/rs/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
EMSDK_QUIET=1 . "/usr/local/emsdk/emsdk_env.sh"
EMCC_CFLAGS="--no-entry -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s MAX_WEBGL_VERSION=2 -s MODULARIZE=1 -s EXPORT_NAME=createRustSkiaModule -s EXPORTED_RUNTIME_METHODS=GL -s ENVIRONMENT=web" cargo build --target=wasm32-unknown-emscripten

View File

@@ -2,9 +2,11 @@ use std::boxed::Box;
use skia_safe::{ use skia_safe::{
gpu::{self, gl::FramebufferInfo, DirectContext}, gpu::{self, gl::FramebufferInfo, DirectContext},
Color, Paint, PaintStyle, Rect, Surface, // Color, Paint, PaintStyle, Rect, Surface,
}; };
use skia_safe as skia;
extern "C" { extern "C" {
pub fn emscripten_GetProcAddress( pub fn emscripten_GetProcAddress(
name: *const ::std::os::raw::c_char, name: *const ::std::os::raw::c_char,
@@ -22,15 +24,15 @@ struct GpuState {
/// structures are not thread safe, so a state must not be shared between different Web Workers. /// structures are not thread safe, so a state must not be shared between different Web Workers.
pub struct State { pub struct State {
gpu_state: GpuState, gpu_state: GpuState,
surface: Surface, surface: skia::Surface,
} }
impl State { impl State {
fn new(gpu_state: GpuState, surface: Surface) -> Self { fn new(gpu_state: GpuState, surface: skia::Surface) -> Self {
State { gpu_state, surface } State { gpu_state, surface }
} }
fn set_surface(&mut self, surface: Surface) { fn set_surface(&mut self, surface: skia::Surface) {
self.surface = surface; self.surface = surface;
} }
} }
@@ -66,7 +68,7 @@ fn create_gpu_state() -> GpuState {
} }
/// Create the Skia surface that will be used for rendering. /// Create the Skia surface that will be used for rendering.
fn create_surface(gpu_state: &mut GpuState, width: i32, height: i32) -> Surface { fn create_surface(gpu_state: &mut GpuState, width: i32, height: i32) -> skia::Surface {
let backend_render_target = let backend_render_target =
gpu::backend_render_targets::make_gl((width, height), 1, 8, gpu_state.framebuffer_info); gpu::backend_render_targets::make_gl((width, height), 1, 8, gpu_state.framebuffer_info);
@@ -81,9 +83,9 @@ fn create_surface(gpu_state: &mut GpuState, width: i32, height: i32) -> Surface
.unwrap() .unwrap()
} }
fn render_rect(surface: &mut Surface, rect: Rect, color: Color) { fn render_rect(surface: &mut skia::Surface, rect: skia::Rect, color: skia::Color) {
let mut paint = Paint::default(); let mut paint = skia::Paint::default();
paint.set_style(PaintStyle::Fill); paint.set_style(skia::PaintStyle::Fill);
paint.set_color(color); paint.set_color(color);
paint.set_anti_alias(true); paint.set_anti_alias(true);
surface.canvas().draw_rect(rect, &paint); surface.canvas().draw_rect(rect, &paint);
@@ -107,24 +109,51 @@ pub unsafe extern "C" fn resize_surface(state: *mut State, width: i32, height: i
state.set_surface(surface); state.set_surface(surface);
} }
/// Draw a black rect at the specified coordinates.
/// # Safety
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn draw_rect( pub extern "C" fn make_color(r: i32, g: i32, b: i32, a: f32) -> Box<Color> {
state: *mut State, Box::new(Color {
left: i32, r: r as u8,
top: i32, g: g as u8,
right: i32, b: b as u8,
bottom: i32, a,
})
}
#[repr(C)]
pub struct Color {
r: u8, r: u8,
g: u8, g: u8,
b: u8, b: u8,
) { a: f32,
let state = unsafe { state.as_mut() }.expect("got an invalid state pointer"); }
let rect = Rect::new(left as f32, top as f32, right as f32, bottom as f32);
let color = Color::from_rgb(r, g, b);
render_rect(&mut state.surface, rect, color); #[repr(C)]
pub struct Rect {
left: f32,
top: f32,
right: f32,
bottom: f32,
}
#[no_mangle]
pub extern "C" fn make_rect(left: f32, top: f32, right: f32, bottom: f32) -> Box<Rect> {
Box::new(Rect {
left,
top,
right,
bottom,
})
}
/// Draws a rect at the specified coordinates with the give ncolor
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn draw_rect(state: *mut State, rect: &Rect, color: &Color) {
let state = unsafe { state.as_mut() }.expect("got an invalid state pointer");
let r = skia::Rect::new(rect.left, rect.top, rect.right, rect.bottom);
let color = skia::Color::from_argb((color.a * 255.0) as u8, color.r, color.g, color.b);
render_rect(&mut state.surface, r, color);
} }
#[no_mangle] #[no_mangle]

View File

@@ -23,6 +23,8 @@
reset-canvas (gobj/get ^js internal-module "_reset_canvas") reset-canvas (gobj/get ^js internal-module "_reset_canvas")
scale (gobj/get ^js internal-module "_scale") scale (gobj/get ^js internal-module "_scale")
flush (gobj/get ^js internal-module "_flush") flush (gobj/get ^js internal-module "_flush")
make-color (gobj/get ^js internal-module "_make_color")
make-rect (gobj/get ^js internal-module "_make_rect")
supported-shapes (filter (fn [shape] (not= (:type shape) :frame)) (vals objects))] supported-shapes (filter (fn [shape] (not= (:type shape) :frame)) (vals objects))]
(js/requestAnimationFrame (fn [] (js/requestAnimationFrame (fn []
@@ -31,9 +33,11 @@
(translate gpu-state (- (:x vbox)) (- (:y vbox))) (translate gpu-state (- (:x vbox)) (- (:y vbox)))
(doseq [shape supported-shapes] (doseq [shape supported-shapes]
(let [sr (:selrect shape) (let [sr (:selrect shape)
[r g b] (cc/hex->rgb (-> shape :fills first :fill-color))] [r g b] (cc/hex->rgb (-> shape :fills first :fill-color))
;; (js/console.log (clj->js shape)) alpha (-> shape :fills first :fill-opacity)
(draw-rect gpu-state (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr) r g b))) color (make-color r g b alpha)
rect (make-rect (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr))]
(draw-rect gpu-state rect color)))
(flush gpu-state))))) (flush gpu-state)))))
(defn set-canvas (defn set-canvas

View File

@@ -28,7 +28,7 @@ var readyPromise = new Promise((resolve, reject) => {
readyPromiseResolve = resolve; readyPromiseResolve = resolve;
readyPromiseReject = reject; readyPromiseReject = reject;
}); });
["_draw_rect","_flush","_init","_main","_reset_canvas","_resize_surface","_scale","_translate","getExceptionMessage","incrementExceptionRefcount","decrementExceptionRefcount","_memory","___indirect_function_table","onRuntimeInitialized"].forEach((prop) => { ["_draw_rect","_flush","_init","_main","_make_color","_make_rect","_reset_canvas","_resize_surface","_scale","_translate","getExceptionMessage","incrementExceptionRefcount","decrementExceptionRefcount","_memory","___indirect_function_table","onRuntimeInitialized"].forEach((prop) => {
if (!Object.getOwnPropertyDescriptor(readyPromise, prop)) { if (!Object.getOwnPropertyDescriptor(readyPromise, prop)) {
Object.defineProperty(readyPromise, prop, { Object.defineProperty(readyPromise, prop, {
get: () => abort('You are getting ' + prop + ' on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js'), get: () => abort('You are getting ' + prop + ' on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js'),
@@ -700,7 +700,7 @@ function createWasm() {
} }
} }
if (!wasmBinaryFile) wasmBinaryFile = findWasmBinary(); wasmBinaryFile ??= findWasmBinary();
// If instantiation fails, reject the module ready promise. // If instantiation fails, reject the module ready promise.
instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject); instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject);
@@ -752,45 +752,51 @@ function isExportedByForceFilesystem(name) {
name === 'removeRunDependency'; name === 'removeRunDependency';
} }
function missingGlobal(sym, msg) { /**
if (typeof globalThis != 'undefined') { * Intercept access to a global symbol. This enables us to give informative
* warnings/errors when folks attempt to use symbols they did not include in
* their build, or no symbols that no longer exist.
*/
function hookGlobalSymbolAccess(sym, func) {
if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) {
Object.defineProperty(globalThis, sym, { Object.defineProperty(globalThis, sym, {
configurable: true, configurable: true,
get() { get() {
warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`); func();
return undefined; return undefined;
} }
}); });
} }
} }
function missingGlobal(sym, msg) {
hookGlobalSymbolAccess(sym, () => {
warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`);
});
}
missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer'); missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer');
missingGlobal('asm', 'Please use wasmExports instead'); missingGlobal('asm', 'Please use wasmExports instead');
function missingLibrarySymbol(sym) { function missingLibrarySymbol(sym) {
if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { hookGlobalSymbolAccess(sym, () => {
Object.defineProperty(globalThis, sym, { // Can't `abort()` here because it would break code that does runtime
configurable: true, // checks. e.g. `if (typeof SDL === 'undefined')`.
get() { var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`;
// Can't `abort()` here because it would break code that does runtime // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in
// checks. e.g. `if (typeof SDL === 'undefined')`. // library.js, which means $name for a JS name with no prefix, or name
var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`; // for a JS name like _name.
// DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in var librarySymbol = sym;
// library.js, which means $name for a JS name with no prefix, or name if (!librarySymbol.startsWith('_')) {
// for a JS name like _name. librarySymbol = '$' + sym;
var librarySymbol = sym; }
if (!librarySymbol.startsWith('_')) { msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`;
librarySymbol = '$' + sym; if (isExportedByForceFilesystem(sym)) {
} msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you';
msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`; }
if (isExportedByForceFilesystem(sym)) { warnOnce(msg);
msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; });
}
warnOnce(msg);
return undefined;
}
});
}
// Any symbol that is not included from the JS library is also (by definition) // Any symbol that is not included from the JS library is also (by definition)
// not exported on the Module object. // not exported on the Module object.
unexportedRuntimeSymbol(sym); unexportedRuntimeSymbol(sym);
@@ -1305,12 +1311,7 @@ function dbg(...args) {
var _emscripten_date_now = () => Date.now(); var _emscripten_date_now = () => Date.now();
var _emscripten_get_now; var _emscripten_get_now = () => performance.now();
// Modern environment where performance.now() is supported:
// N.B. a shorter form "_emscripten_get_now = performance.now;" is
// unfortunately not allowed even in current browsers (e.g. FF Nightly 75).
_emscripten_get_now = () => performance.now();
;
var GLctx; var GLctx;
@@ -5641,6 +5642,8 @@ function dbg(...args) {
}, },
filesystems:null, filesystems:null,
syncFSRequests:0, syncFSRequests:0,
readFiles:{
},
FSStream:class { FSStream:class {
constructor() { constructor() {
// TODO(https://github.com/emscripten-core/emscripten/issues/21414): // TODO(https://github.com/emscripten-core/emscripten/issues/21414):
@@ -6540,7 +6543,6 @@ function dbg(...args) {
stream.stream_ops.open(stream); stream.stream_ops.open(stream);
} }
if (Module['logReadFiles'] && !(flags & 1)) { if (Module['logReadFiles'] && !(flags & 1)) {
if (!FS.readFiles) FS.readFiles = {};
if (!(path in FS.readFiles)) { if (!(path in FS.readFiles)) {
FS.readFiles[path] = 1; FS.readFiles[path] = 1;
} }
@@ -6961,7 +6963,7 @@ function dbg(...args) {
createDevice(parent, name, input, output) { createDevice(parent, name, input, output) {
var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name);
var mode = FS_getMode(!!input, !!output); var mode = FS_getMode(!!input, !!output);
if (!FS.createDevice.major) FS.createDevice.major = 64; FS.createDevice.major ??= 64;
var dev = FS.makedev(FS.createDevice.major++, 0); var dev = FS.makedev(FS.createDevice.major++, 0);
// Create a fake device that a set of stream ops to emulate // Create a fake device that a set of stream ops to emulate
// the old behavior. // the old behavior.
@@ -8563,7 +8565,9 @@ var wasmExports = createWasm();
var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors', 0); var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors', 0);
var _init = Module['_init'] = createExportWrapper('init', 2); var _init = Module['_init'] = createExportWrapper('init', 2);
var _resize_surface = Module['_resize_surface'] = createExportWrapper('resize_surface', 3); var _resize_surface = Module['_resize_surface'] = createExportWrapper('resize_surface', 3);
var _draw_rect = Module['_draw_rect'] = createExportWrapper('draw_rect', 8); var _make_color = Module['_make_color'] = createExportWrapper('make_color', 4);
var _make_rect = Module['_make_rect'] = createExportWrapper('make_rect', 4);
var _draw_rect = Module['_draw_rect'] = createExportWrapper('draw_rect', 3);
var _flush = Module['_flush'] = createExportWrapper('flush', 1); var _flush = Module['_flush'] = createExportWrapper('flush', 1);
var _translate = Module['_translate'] = createExportWrapper('translate', 3); var _translate = Module['_translate'] = createExportWrapper('translate', 3);
var _scale = Module['_scale'] = createExportWrapper('scale', 3); var _scale = Module['_scale'] = createExportWrapper('scale', 3);
@@ -8613,10 +8617,10 @@ var dynCall_iiiiij = Module['dynCall_iiiiij'] = createExportWrapper('dynCall_iii
var dynCall_iiiiijj = Module['dynCall_iiiiijj'] = createExportWrapper('dynCall_iiiiijj', 9); var dynCall_iiiiijj = Module['dynCall_iiiiijj'] = createExportWrapper('dynCall_iiiiijj', 9);
var dynCall_iiiiiijj = Module['dynCall_iiiiiijj'] = createExportWrapper('dynCall_iiiiiijj', 10); var dynCall_iiiiiijj = Module['dynCall_iiiiiijj'] = createExportWrapper('dynCall_iiiiiijj', 10);
function invoke_vii(index,a1,a2) { function invoke_iii(index,a1,a2) {
var sp = stackSave(); var sp = stackSave();
try { try {
getWasmTableEntry(index)(a1,a2); return getWasmTableEntry(index)(a1,a2);
} catch(e) { } catch(e) {
stackRestore(sp); stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e; if (!(e instanceof EmscriptenEH)) throw e;
@@ -8635,6 +8639,17 @@ function invoke_ii(index,a1) {
} }
} }
function invoke_vii(index,a1,a2) {
var sp = stackSave();
try {
getWasmTableEntry(index)(a1,a2);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
_setThrew(1, 0);
}
}
function invoke_viii(index,a1,a2,a3) { function invoke_viii(index,a1,a2,a3) {
var sp = stackSave(); var sp = stackSave();
try { try {
@@ -8657,10 +8672,10 @@ function invoke_vi(index,a1) {
} }
} }
function invoke_iii(index,a1,a2) { function invoke_iiii(index,a1,a2,a3) {
var sp = stackSave(); var sp = stackSave();
try { try {
return getWasmTableEntry(index)(a1,a2); return getWasmTableEntry(index)(a1,a2,a3);
} catch(e) { } catch(e) {
stackRestore(sp); stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e; if (!(e instanceof EmscriptenEH)) throw e;
@@ -8690,17 +8705,6 @@ function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6) {
} }
} }
function invoke_iiii(index,a1,a2,a3) {
var sp = stackSave();
try {
return getWasmTableEntry(index)(a1,a2,a3);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
_setThrew(1, 0);
}
}
function invoke_iiiii(index,a1,a2,a3,a4) { function invoke_iiiii(index,a1,a2,a3,a4) {
var sp = stackSave(); var sp = stackSave();
try { try {
@@ -9048,12 +9052,14 @@ var missingLibrarySymbols = [
'setImmediateWrapped', 'setImmediateWrapped',
'clearImmediateWrapped', 'clearImmediateWrapped',
'polyfillSetImmediate', 'polyfillSetImmediate',
'registerPostMainLoop',
'registerPreMainLoop',
'getPromise', 'getPromise',
'makePromise', 'makePromise',
'idsToPromises', 'idsToPromises',
'makePromiseCallback', 'makePromiseCallback',
'Browser_asyncPrepareDataCounter', 'Browser_asyncPrepareDataCounter',
'setMainLoop', 'safeRequestAnimationFrame',
'isLeapYear', 'isLeapYear',
'ydayFromDate', 'ydayFromDate',
'arraySum', 'arraySum',